From 56f1c452fcdaf7631d893fd5dcbf44d27a7fc085 Mon Sep 17 00:00:00 2001 From: Furniel Date: Sun, 24 Dec 2017 12:14:41 +0200 Subject: [PATCH] v 0.9.6 --- BrowserCache_Environment.php | 32 +- Cache.php | 4 - CacheFlush.php | 8 +- CacheFlush_Locally.php | 22 +- Cache_File_Generic.php | 59 +- Cache_Memcache.php | 8 + Cache_Memcached.php | 8 + Cache_Redis.php | 14 +- CdnEngine.php | 4 - CdnEngine_Azure.php | 11 +- CdnEngine_Base.php | 14 +- CdnEngine_Ftp.php | 10 +- CdnEngine_GoogleDrive.php | 193 +- CdnEngine_Mirror_Akamai.php | 4 - CdnEngine_Mirror_Cotendo.php | 4 - CdnEngine_Mirror_Edgecast.php | 4 - CdnEngine_Mirror_MaxCdn.php | 172 +- CdnEngine_Mirror_Netdna.php | 178 -- CdnEngine_RackSpaceCloudFiles.php | 10 +- CdnEngine_S3.php | 51 +- CdnEngine_S3_Cf.php | 19 +- CdnEngine_S3_Compatible.php | 12 +- Cdn_AdminActions.php | 357 +-- Cdn_AdminNotes.php | 22 +- Cdn_CloudFrontFsd_Page.php | 18 - Cdn_ConfigLabels.php | 2 + Cdn_Core.php | 31 +- Cdn_Core_Admin.php | 14 +- Cdn_Environment.php | 98 +- Cdn_Fsd_Core.php | 41 - Cdn_GeneralPage_View.php | 4 +- Cdn_Highwinds_Page_View.js | 2 +- Cdn_Highwinds_Page_View.php | 6 +- ...ghwinds_Popup_View_ConfigureCnamesForm.php | 2 +- Cdn_MaxCdnFsd_Page.php | 18 - Cdn_MaxCdn_Page.php | 28 + Cdn_MaxCdn_Page_View.js | 68 + Cdn_MaxCdn_Page_View.php | 96 + Cdn_MaxCdn_Popup.php | 282 ++ Cdn_MaxCdn_Popup_View_Intro.php | 39 + Cdn_MaxCdn_Popup_View_Success.php | 23 + Cdn_MaxCdn_Popup_View_Zone.php | 74 + Cdn_MaxCdn_Popup_View_Zones.php | 53 + Cdn_Page.php | 32 - Cdn_Page_View_Fsd_HeaderActions.php | 2 +- Cdn_Plugin.php | 98 +- Cdn_Plugin_Admin.php | 111 +- Cdn_Plugin_WidgetMaxCdn.php | 9 - Cdn_Plugin_WidgetNetDna.php | 191 -- Cdn_RackSpaceCdn_Page_View.js | 2 +- Cdn_RackSpaceCdn_Page_View.php | 6 +- ...ckSpaceCdn_Popup_View_ConfigureDomains.php | 2 +- Cdn_RackSpaceCloudFiles_Page_View.php | 6 +- Cdn_Util.php | 25 +- ...sd_CacheFlush.php => Cdnfsd_CacheFlush.php | 28 +- ...ntFsd_Api.php => Cdnfsd_CloudFront_Api.php | 2 +- ...Engine.php => Cdnfsd_CloudFront_Engine.php | 6 +- Cdnfsd_CloudFront_Page.php | 18 + ..._View.js => Cdnfsd_CloudFront_Page_View.js | 2 +- ...iew.php => Cdnfsd_CloudFront_Page_View.php | 18 +- ...d_Popup.php => Cdnfsd_CloudFront_Popup.php | 38 +- ...fsd_CloudFront_Popup_View_Distribution.php | 0 ...sd_CloudFront_Popup_View_Distributions.php | 0 ... => Cdnfsd_CloudFront_Popup_View_Intro.php | 4 +- ...> Cdnfsd_CloudFront_Popup_View_Success.php | 0 Cdnfsd_Core.php | 54 + Cdnfsd_GeneralPage_View.php | 37 + Cdnfsd_Limelight_Api.php | 101 + Cdnfsd_Limelight_Engine.php | 85 + Cdnfsd_Limelight_Page.php | 18 + Cdnfsd_Limelight_Page_View.js | 42 + Cdnfsd_Limelight_Page_View.php | 43 + Cdnfsd_Limelight_Popup.php | 68 + Cdnfsd_Limelight_Popup_View_Intro.php | 49 + Cdnfsd_Limelight_Popup_View_Success.php | 22 + ...Fsd_Engine.php => Cdnfsd_MaxCdn_Engine.php | 2 +- Cdnfsd_MaxCdn_Page.php | 20 + ...Page_View.js => Cdnfsd_MaxCdn_Page_View.js | 18 +- ...ge_View.php => Cdnfsd_MaxCdn_Page_View.php | 21 +- ...dnFsd_Popup.php => Cdnfsd_MaxCdn_Popup.php | 68 +- ....php => Cdnfsd_MaxCdn_Popup_View_Intro.php | 0 ...hp => Cdnfsd_MaxCdn_Popup_View_Success.php | 0 ...e.php => Cdnfsd_MaxCdn_Popup_View_Zone.php | 8 +- ....php => Cdnfsd_MaxCdn_Popup_View_Zones.php | 0 Cdnfsd_Page_View_Header.php | 15 + Cdnfsd_Plugin.php | 61 + Cdnfsd_Plugin_Admin.php | 91 + Cdn_Fsd_Util.php => Cdnfsd_Util.php | 10 +- Cli.php | 26 + Config.php | 73 +- ConfigCache.php | 108 + ConfigCompiler.php | 108 +- ConfigDbStorage.php | 390 +++ ConfigKeys.php | 132 +- ConfigState.php | 2 - ConfigUtil.php | 81 + DbCache_Environment.php | 8 +- DbCache_Wpdb.php | 44 +- DbCache_WpdbInjection.php | 8 + DbCache_WpdbInjection_QueryCaching.php | 40 +- Dispatcher.php | 15 +- Enterprise_CacheFlush_MakeSnsEvent.php | 14 +- Enterprise_Dbcache_WpdbInjection_Cluster.php | 159 +- Extension_Amp_Plugin.php | 10 +- Extension_CloudFlare_Api.php | 2 +- Extension_CloudFlare_Cdn_Page_View.php | 31 + Extension_CloudFlare_Page.php | 12 +- Extension_CloudFlare_Plugin_Admin.php | 64 +- Extension_CloudFlare_Popup_View_Zones.php | 80 +- Extension_CloudFlare_SettingsForUi.php | 4 +- Extension_FragmentCache_Plugin_Admin.php | 15 +- Extension_FragmentCache_WpObjectCache.php | 27 +- Extension_NewRelic_Plugin.php | 43 +- Extension_NewRelic_Plugin_Admin.php | 13 +- Extension_NewRelic_Popup_View.js | 16 +- Extension_Swarmify_Page.php | 9 +- Extension_WordPressSeo_Plugin_Admin.php | 12 - Extensions_Plugin_Admin.php | 14 +- Generic_AdminActions_Config.php | 42 +- Generic_AdminActions_Default.php | 15 +- Generic_AdminActions_EdgeMode.php | 28 - Generic_AdminActions_Flush.php | 2 +- Generic_AdminActions_Test.php | 10 +- Generic_ConfigLabels.php | 1 + Generic_Environment.php | 35 +- Generic_Faq.php | 2 - Generic_GeneralPage_View_ShowEdge.js | 6 +- Generic_GeneralPage_View_ShowSupportUs.js | 9 +- Generic_Page_Dashboard_View.css | 53 +- Generic_Page_General.php | 1 - Generic_Plugin.php | 37 +- Generic_Plugin_Admin.php | 17 +- Generic_Plugin_AdminCompatibility.php | 27 - Generic_Plugin_AdminNotifications.php | 29 - Minify_ContentMinifier.php | 2 +- Minify_Plugin.php | 48 +- Minify_Plugin_Admin.php | 4 +- ModuleStatus.php | 16 - ObjectCache_Environment.php | 8 +- ObjectCache_WpObjectCache_Regular.php | 79 +- PageSpeed_Api.php | 18 +- PageSpeed_Plugin_Widget.php | 6 +- PgCache_ConfigLabels.php | 4 + PgCache_ContentGrabber.php | 436 ++- PgCache_Environment.php | 182 +- PgCache_Flush.php | 22 +- PgCache_Page.php | 4 +- PgCache_Page_CookieGroups.php | 78 + PgCache_Page_CookieGroups_View.js | 96 + PgCache_Page_CookieGroups_View.php | 113 + PgCache_Plugin_Admin.php | 43 + Root_AdminActions.php | 1 - Root_AdminActivation.php | 2 +- Root_AdminMenu.php | 87 +- Root_Loader.php | 18 +- Support_AdminActions.php | 2 - Util_Admin.php | 28 +- Util_AttachToActions.php | 12 + Util_Debug.php | 9 + Util_Environment.php | 60 +- Util_PageUrls.php | 4 +- Util_Rule.php | 12 +- Util_RuleSnippet.php | 43 +- Util_Ui.php | 2 +- inc/lightbox/edge.php | 24 - inc/lightbox/self_test.php | 121 +- inc/lightbox/support_us.php | 6 +- inc/lightbox/upgrade.php | 4 +- inc/options/about.php | 2 +- inc/options/cdn.php | 18 +- inc/options/cdn/akamai.php | 2 +- inc/options/cdn/att.php | 4 +- inc/options/cdn/azure.php | 2 +- inc/options/cdn/cf.php | 31 +- inc/options/cdn/cf2.php | 2 +- inc/options/cdn/common/cnames.php | 17 +- inc/options/cdn/cotendo.php | 4 +- inc/options/cdn/edgecast.php | 4 +- inc/options/cdn/ftp.php | 2 +- inc/options/cdn/maxcdn.php | 89 - inc/options/cdn/mirror.php | 4 +- inc/options/cdn/netdna.php | 89 - inc/options/cdn/rscf.php | 2 +- inc/options/cdn/s3.php | 30 +- inc/options/cdn/s3_compatible.php | 2 +- inc/options/common/header.php | 2 +- inc/options/dashboard.php | 4 +- inc/options/general.php | 43 +- inc/options/minify.php | 4 +- inc/options/minify/yuijs2.php | 12 +- inc/options/pgcache.php | 48 +- inc/popup/cdn_purge.php | 2 +- inc/widget/maxcdn_signup.php | 31 +- inc/widget/netdna.php | 64 - inc/widget/netdna_signup.php | 53 - ini/config-db-sample.php | 34 + ini/dbcluster-config-sample.php | 324 +-- ini/web.config | 31 +- languages/faq-en_US.xml | 7 +- languages/faq-pro-en_US.xml | 8 +- languages/w3-total-cache.pot | 27 +- lib/Minify/CSSmin.php | 758 ------ lib/Minify/Minify/CSS/UriRewriter.php | 3 +- lib/Minify/Minify/Cache/File.php | 5 +- lib/Minify/Minify/ClosureCompiler.php | 9 +- lib/Minify/Minify/HTML.php | 2 +- lib/Minify/Minify/YUICompressor.php | 54 +- .../Colors.php | 155 ++ .../Command.php | 223 ++ .../Minifier.php | 862 ++++++ .../Utils.php | 149 ++ lib/NetDNA/NetDNA.php | 831 +++--- lib/S3.php | 2356 ++++++++++++++--- pub/css/lightbox.css | 9 + pub/css/options.css | 363 +-- pub/js/lightbox.js | 94 +- pub/js/options.js | 297 +-- pub/opcache.php | 2 +- readme.txt | 31 +- w3-total-cache-api.php | 35 +- w3-total-cache.php | 2 +- 221 files changed, 9203 insertions(+), 5055 deletions(-) delete mode 100644 CdnEngine_Mirror_Netdna.php delete mode 100644 Cdn_CloudFrontFsd_Page.php delete mode 100644 Cdn_Fsd_Core.php delete mode 100644 Cdn_MaxCdnFsd_Page.php create mode 100644 Cdn_MaxCdn_Page.php create mode 100644 Cdn_MaxCdn_Page_View.js create mode 100644 Cdn_MaxCdn_Page_View.php create mode 100644 Cdn_MaxCdn_Popup.php create mode 100644 Cdn_MaxCdn_Popup_View_Intro.php create mode 100644 Cdn_MaxCdn_Popup_View_Success.php create mode 100644 Cdn_MaxCdn_Popup_View_Zone.php create mode 100644 Cdn_MaxCdn_Popup_View_Zones.php delete mode 100644 Cdn_Plugin_WidgetNetDna.php rename Cdn_Fsd_CacheFlush.php => Cdnfsd_CacheFlush.php (91%) rename Cdn_CloudFrontFsd_Api.php => Cdnfsd_CloudFront_Api.php (99%) rename Cdn_CloudFrontFsd_Engine.php => Cdnfsd_CloudFront_Engine.php (88%) create mode 100644 Cdnfsd_CloudFront_Page.php rename Cdn_CloudFrontFsd_Page_View.js => Cdnfsd_CloudFront_Page_View.js (98%) rename Cdn_CloudFrontFsd_Page_View.php => Cdnfsd_CloudFront_Page_View.php (68%) rename Cdn_CloudFrontFsd_Popup.php => Cdnfsd_CloudFront_Popup.php (89%) rename Cdn_CloudFrontFsd_Popup_View_Distribution.php => Cdnfsd_CloudFront_Popup_View_Distribution.php (100%) rename Cdn_CloudFrontFsd_Popup_View_Distributions.php => Cdnfsd_CloudFront_Popup_View_Distributions.php (100%) rename Cdn_CloudFrontFsd_Popup_View_Intro.php => Cdnfsd_CloudFront_Popup_View_Intro.php (94%) rename Cdn_CloudFrontFsd_Popup_View_Success.php => Cdnfsd_CloudFront_Popup_View_Success.php (100%) create mode 100644 Cdnfsd_Core.php create mode 100644 Cdnfsd_GeneralPage_View.php create mode 100644 Cdnfsd_Limelight_Api.php create mode 100644 Cdnfsd_Limelight_Engine.php create mode 100644 Cdnfsd_Limelight_Page.php create mode 100644 Cdnfsd_Limelight_Page_View.js create mode 100644 Cdnfsd_Limelight_Page_View.php create mode 100644 Cdnfsd_Limelight_Popup.php create mode 100644 Cdnfsd_Limelight_Popup_View_Intro.php create mode 100644 Cdnfsd_Limelight_Popup_View_Success.php rename Cdn_MaxCdnFsd_Engine.php => Cdnfsd_MaxCdn_Engine.php (97%) create mode 100644 Cdnfsd_MaxCdn_Page.php rename Cdn_MaxCdnFsd_Page_View.js => Cdnfsd_MaxCdn_Page_View.js (81%) rename Cdn_MaxCdnFsd_Page_View.php => Cdnfsd_MaxCdn_Page_View.php (66%) rename Cdn_MaxCdnFsd_Popup.php => Cdnfsd_MaxCdn_Popup.php (78%) rename Cdn_MaxCdnFsd_Popup_View_Intro.php => Cdnfsd_MaxCdn_Popup_View_Intro.php (100%) rename Cdn_MaxCdnFsd_Popup_View_Success.php => Cdnfsd_MaxCdn_Popup_View_Success.php (100%) rename Cdn_MaxCdnFsd_Popup_View_Zone.php => Cdnfsd_MaxCdn_Popup_View_Zone.php (80%) rename Cdn_MaxCdnFsd_Popup_View_Zones.php => Cdnfsd_MaxCdn_Popup_View_Zones.php (100%) create mode 100644 Cdnfsd_Page_View_Header.php create mode 100644 Cdnfsd_Plugin.php create mode 100644 Cdnfsd_Plugin_Admin.php rename Cdn_Fsd_Util.php => Cdnfsd_Util.php (78%) create mode 100644 ConfigCache.php create mode 100644 ConfigDbStorage.php create mode 100644 ConfigUtil.php create mode 100644 Extension_CloudFlare_Cdn_Page_View.php delete mode 100644 Generic_AdminActions_EdgeMode.php create mode 100644 PgCache_Page_CookieGroups.php create mode 100644 PgCache_Page_CookieGroups_View.js create mode 100644 PgCache_Page_CookieGroups_View.php delete mode 100644 inc/lightbox/edge.php delete mode 100644 inc/options/cdn/maxcdn.php delete mode 100644 inc/options/cdn/netdna.php delete mode 100644 inc/widget/netdna.php delete mode 100644 inc/widget/netdna_signup.php create mode 100644 ini/config-db-sample.php delete mode 100644 lib/Minify/CSSmin.php create mode 100644 lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php create mode 100644 lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php create mode 100644 lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php create mode 100644 lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php diff --git a/BrowserCache_Environment.php b/BrowserCache_Environment.php index 0fc2c79..e677615 100644 --- a/BrowserCache_Environment.php +++ b/BrowserCache_Environment.php @@ -316,7 +316,7 @@ private function rules_cache_generate_apache( $config ) { $rules .= " BrowserMatch \\bMSI[E] !no-gzip !gzip-only-text/html\n"; $rules .= " \n"; } - if ( version_compare( $this->_get_server_version(), '2.3.7', '>=' ) ) { + if ( version_compare( Util_Environment::get_server_version(), '2.3.7', '>=' ) ) { $rules .= " \n"; } $rules .= " AddOutputFilterByType DEFLATE " . implode( ' ', $compression_types ) . "\n"; @@ -325,7 +325,7 @@ private function rules_cache_generate_apache( $config ) { $rules .= " AddOutputFilter DEFLATE js css htm html xml\n"; $rules .= " \n"; - if ( version_compare( $this->_get_server_version(), '2.3.7', '>=' ) ) { + if ( version_compare( Util_Environment::get_server_version(), '2.3.7', '>=' ) ) { $rules .= " \n"; } $rules .= "\n"; @@ -585,10 +585,12 @@ private function _rules_cache_generate_nginx_for_type( $config, &$rules, $mime_types, $section ) { $expires = $config->get_boolean( 'browsercache.' . $section . '.expires' ); + $etag = $config->get_boolean( 'browsercache.' . $section . '.etag' ); $cache_control = $config->get_boolean( 'browsercache.' . $section . '.cache.control' ); $w3tc = $config->get_boolean( 'browsercache.' . $section . '.w3tc' ); + $last_modified = $config->get_boolean( 'browsercache.' . $section . '.last_modified' ); - if ( $expires || $cache_control || $w3tc ) { + if ( $etag || $expires || $cache_control || $w3tc || !$last_modified ) { $lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' ); $extensions = array_keys( $mime_types ); @@ -604,6 +606,18 @@ private function _rules_cache_generate_nginx_for_type( $config, &$rules, if ( $expires ) { $rules .= " expires " . $lifetime . "s;\n"; } + if ( version_compare( Util_Environment::get_server_version(), '1.3.3', '>=' ) ) { + if ( $etag ) { + $rules .= " etag on;\n"; + } else { + $rules .= " etag off;\n"; + } + } + if ( $last_modified ) { + $rules .= " if_modified_since exact;\n"; + } else { + $rules .= " if_modified_since off;\n"; + } $add_header_rules = ''; @@ -794,18 +808,6 @@ private function rules_no404wp_generate_apache( $config ) { return $rules; } - /** - * Returns the apache, nginx version - * - * @return string - */ - private function _get_server_version() { - $sig= explode( '/', $_SERVER['SERVER_SOFTWARE'] ); - $temp = isset( $sig[1] ) ? explode( ' ', $sig[1] ) : array( '0' ); - $version = $temp[0]; - return $version; - } - /** * Takes an array of extensions single per row and/or extensions delimited by | * diff --git a/Cache.php b/Cache.php index 6b6bebe..fa9a94c 100644 --- a/Cache.php +++ b/Cache.php @@ -166,10 +166,6 @@ static public function engine_name( $engine, $module = '' ) { $engine_name = 'mirror'; break; - case 'netdna': - $engine_name = 'netdna'; - break; - case 'maxcdn': $engine_name = 'maxcdn'; break; diff --git a/CacheFlush.php b/CacheFlush.php index 91a7676..e9c61a3 100644 --- a/CacheFlush.php +++ b/CacheFlush.php @@ -128,8 +128,8 @@ function opcache_flush_file( $filename ) { /** * Purges/Flushes post page */ - function flush_post( $post_id ) { - return $this->_executor->flush_post( $post_id ); + function flush_post( $post_id, $extras = null ) { + return $this->_executor->flush_post( $post_id, $extras ); } /** @@ -162,12 +162,12 @@ function flush_all( $extras = null ) { /** * Purges/Flushes url */ - function flush_url( $url ) { + function flush_url( $url, $extras = null ) { static $flushed_urls = array(); if ( !in_array( $url, $flushed_urls ) ) { $flushed_urls[] = $url; - return $this->_executor->flush_url( $url ); + return $this->_executor->flush_url( $url, $extras ); } return true; } diff --git a/CacheFlush_Locally.php b/CacheFlush_Locally.php index 65128b2..99a060c 100644 --- a/CacheFlush_Locally.php +++ b/CacheFlush_Locally.php @@ -83,7 +83,7 @@ function minifycache_flush( $extras = array() ) { function minifycache_flush_all( $extras = array() ) { if ( $extras['minify'] == 'purge_map' ) delete_option( 'w3tc_minify' ); - + $this->minifycache_flush( $extras ); } @@ -156,8 +156,10 @@ function opcache_flush_file( $filename ) { /** * Purges/Flushes post from page cache, varnish and cdn cache */ - function flush_post( $post_id ) { - do_action( 'w3tc_flush_post', $post_id ); + function flush_post( $post_id, $extras = null ) { + $do_flush = apply_filters( 'w3tc_preflush_post', true, $extras ); + if ( $do_flush ) + do_action( 'w3tc_flush_post', $post_id ); } /** @@ -165,7 +167,9 @@ function flush_post( $post_id ) { * When global changes affect whole content but not internal data structures */ function flush_posts( $extras = null ) { - do_action( 'w3tc_flush_posts', $extras ); + $do_flush = apply_filters( 'w3tc_preflush_posts', true, $extras ); + if ( $do_flush ) + do_action( 'w3tc_flush_posts', $extras ); } /** @@ -198,14 +202,18 @@ function flush_all( $extras ) { $default_actions_added = true; } - do_action( 'w3tc_flush_all', $extras ); + $do_flush = apply_filters( 'w3tc_preflush_all', true, $extras ); + if ( $do_flush ) + do_action( 'w3tc_flush_all', $extras ); } /** * Purges/Flushes url from page cache, varnish and cdn cache */ - function flush_url( $url ) { - do_action( 'w3tc_flush_url', $url ); + function flush_url( $url, $extras = null ) { + $do_flush = apply_filters( 'w3tc_preflush_url', true, $extras ); + if ( $do_flush ) + do_action( 'w3tc_flush_url', $url ); } /** diff --git a/Cache_File_Generic.php b/Cache_File_Generic.php index 76c7bcd..1afe337 100644 --- a/Cache_File_Generic.php +++ b/Cache_File_Generic.php @@ -2,13 +2,7 @@ namespace W3TC; /** - * Generic file cache - */ - - - -/** - * class Cache_File_Generic + * Disk:Enhanced file cache */ class Cache_File_Generic extends Cache_File { /** @@ -56,7 +50,7 @@ function set( $key, $var, $expire = 0, $group = '' ) { $tmppath = $path . '.' . getmypid(); - $fp = @fopen( $tmppath, 'w' ); + $fp = @fopen( $tmppath, 'wb' ); if ( !$fp ) return false; @@ -83,16 +77,43 @@ function set( $key, $var, $expire = 0, $group = '' ) { $old_entry_path = $path . '_old'; @unlink( $old_entry_path ); - if ( Util_Environment::is_apache() && isset( $var['headers'] ) && - isset( $var['headers']['Content-Type'] ) && - substr( $var['headers']['Content-Type'], 0, 8 ) == 'text/xml' ) { - file_put_contents( dirname( $path ) . '/.htaccess', - "\n" . - " RemoveType .html_gzip\n" . - " AddType text/xml .html_gzip\n" . - " RemoveType .html\n" . - " AddType text/xml .html\n". - "" ); + if ( Util_Environment::is_apache() && isset( $var['headers'] ) ) { + $rules = ''; + + if ( isset( $var['headers']['Content-Type'] ) && + substr( $var['headers']['Content-Type'], 0, 8 ) == 'text/xml' ) { + + $rules .= "\n"; + $rules .= " RemoveType .html_gzip\n"; + $rules .= " AddType text/xml .html_gzip\n"; + $rules .= " RemoveType .html\n"; + $rules .= " AddType text/xml .html\n"; + $rules .= "\n"; + } + + if ( isset( $var['headers'] ) ) { + $links = ''; + + foreach ( $var['headers'] as $h ) { + if ( isset($h['n']) && isset($h['v']) && $h['n'] == 'Link' ) { + $value = $h['v']; + if ( false !== strpos( $value, 'rel=preload' ) ) { + $links .= " Header add Link '" . trim($value) . "'\n"; + } + } + } + + if ( !empty( $links) ) { + $rules .= "\n"; + $rules .= " Header unset Link\n"; + $rules .= $links; + $rules .= "\n"; + } + } + + if ( !empty($rules) ) { + @file_put_contents( dirname( $path ) . '/.htaccess', $rules ); + } } return true; @@ -147,7 +168,7 @@ private function _read( $path ) { if ( !is_readable( $path ) ) return null; - $fp = @fopen( $path, 'r' ); + $fp = @fopen( $path, 'rb' ); if ( !$fp ) return null; diff --git a/Cache_Memcache.php b/Cache_Memcache.php index f6d01eb..a05d34e 100644 --- a/Cache_Memcache.php +++ b/Cache_Memcache.php @@ -44,6 +44,14 @@ function __construct( $config ) { return false; } + // when disabled - no extra requests are made to obtain key version, + // but flush operations not supported as a result + // group should be always empty + if ( isset( $config['key_version_mode'] ) && + $config['key_version_mode'] == 'disabled' ) { + $this->_key_version[''] = 1; + } + return true; } diff --git a/Cache_Memcached.php b/Cache_Memcached.php index 401bd56..1b873d1 100644 --- a/Cache_Memcached.php +++ b/Cache_Memcached.php @@ -82,6 +82,14 @@ private function initialize( $config ) { $this->_memcache->setSaslAuthData( $config['username'], $config['password'] ); + // when disabled - no extra requests are made to obtain key version, + // but flush operations not supported as a result + // group should be always empty + if ( isset( $config['key_version_mode'] ) && + $config['key_version_mode'] == 'disabled' ) { + $this->_key_version[''] = 1; + } + return true; } diff --git a/Cache_Redis.php b/Cache_Redis.php index 9de3901..71b4aff 100644 --- a/Cache_Redis.php +++ b/Cache_Redis.php @@ -25,6 +25,14 @@ function __construct( $config ) { $this->_servers = (array)$config['servers']; $this->_password = $config['password']; $this->_dbid = $config['dbid']; + + // when disabled - no extra requests are made to obtain key version, + // but flush operations not supported as a result + // group should be always empty + if ( isset( $config['key_version_mode'] ) && + $config['key_version_mode'] == 'disabled' ) { + $this->_key_version[''] = 1; + } } /** @@ -325,14 +333,16 @@ private function _get_accessor( $key ) { if ( substr( $server, 0, 5 ) == 'unix:' ) { if ( $this->_persistent ) - $accessor->pconnect( trim( substr( $server, 5 ) ) ); + $accessor->pconnect( trim( substr( $server, 5 ) ), + null, null, $this->_instance_id . '_' . $this->_dbid ); else $accessor->connect( trim( substr( $server, 5 ) ) ); } else { list( $ip, $port ) = explode( ':', $server ); if ( $this->_persistent ) - $accessor->pconnect( trim( $ip ), (integer) trim( $port ) ); + $accessor->pconnect( trim( $ip ), (integer) trim( $port ), + null, $this->_instance_id . '_' . $this->_dbid ); else $accessor->connect( trim( $ip ), (integer) trim( $port ) ); } diff --git a/CdnEngine.php b/CdnEngine.php index e452382..4bb85bc 100644 --- a/CdnEngine.php +++ b/CdnEngine.php @@ -67,10 +67,6 @@ static function instance( $engine, $config = array() ) { $instances[$instance_key] = new CdnEngine_Mirror( $config ); break; - case 'netdna': - $instances[$instance_key] = new CdnEngine_Mirror_Netdna( $config ); - break; - case 'rackspace_cdn': $instances[$instance_key] = new CdnEngine_Mirror_RackSpaceCdn( $config ); break; diff --git a/CdnEngine_Azure.php b/CdnEngine_Azure.php index 813e064..4395ab8 100644 --- a/CdnEngine_Azure.php +++ b/CdnEngine_Azure.php @@ -90,10 +90,15 @@ function upload( $files, &$results, $force_rewrite = false, } foreach ( $files as $file ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - break; - $remote_path = $file['remote_path']; + $local_path = $file['local_path']; + + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) { + return 'timeout'; + } + } $results[] = $this->_upload( $file, $force_rewrite ); } diff --git a/CdnEngine_Base.php b/CdnEngine_Base.php index ef91f52..d11ce98 100644 --- a/CdnEngine_Base.php +++ b/CdnEngine_Base.php @@ -179,7 +179,15 @@ function get_domain( $path = '' ) { break; default: - if ( $count > 4 ) { + if ( !isset( $domains[0] ) ) { + $scheme = $this->_get_scheme(); + if ( 'https' == $scheme && isset( $domains['https_default'] ) ) { + return $domains['https_default']; + } else { + return isset( $domains['http_default'] ) ? $domains['http_default'] : + $domains['https_default']; + } + } elseif ( $count > 4 ) { $domain = $this->_get_domain( array_slice( $domains, 4 ), $path ); } else { @@ -579,6 +587,10 @@ function _is_js_footer( $path ) { */ function _get_domain( $domains, $path ) { $count = count( $domains ); + if ( isset( $domains['http_default'] ) ) + $count--; + if ( isset( $domains['https_default'] ) ) + $count--; if ( $count ) { /** diff --git a/CdnEngine_Ftp.php b/CdnEngine_Ftp.php index 4e7be26..945ec26 100644 --- a/CdnEngine_Ftp.php +++ b/CdnEngine_Ftp.php @@ -164,12 +164,16 @@ function upload( $files, &$results, $force_rewrite = false, } foreach ( $files as $file ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - break; - $local_path = $file['local_path']; $remote_path = $file['remote_path']; + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) { + return 'timeout'; + } + } + if ( !file_exists( $local_path ) ) { $results[] = $this->_get_result( $local_path, $remote_path, W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file ); diff --git a/CdnEngine_GoogleDrive.php b/CdnEngine_GoogleDrive.php index 49d8d95..16d43eb 100644 --- a/CdnEngine_GoogleDrive.php +++ b/CdnEngine_GoogleDrive.php @@ -11,6 +11,7 @@ class CdnEngine_GoogleDrive extends CdnEngine_Base { private $_root_url; private $_service; + private $_tablename_pathmap; @@ -28,6 +29,9 @@ function __construct( $config = array() ) { $this->_root_url = rtrim( $config['root_url'], '/' ) . '/'; $this->_new_access_token_callback = $config['new_access_token_callback']; + global $wpdb; + $this->_tablename_pathmap = $wpdb->base_prefix . W3TC_CDN_TABLE_PATHMAP; + try { $this->_init_service( $config['access_token'] ); } catch ( \Exception $e ) { @@ -79,7 +83,7 @@ private function _refresh_token() { function upload( $files, &$results, $force_rewrite = false, $timeout_time = NULL ) { if ( is_null( $this->_service ) ) return false; - + $allow_refresh_token = true; $result = true; @@ -96,8 +100,10 @@ function upload( $files, &$results, $force_rewrite = false, $timeout_time = NULL } if ( $r != 'success' ) $result = false; - if ( $r == 'timeout' ) - break; + if ( $r == 'timeout' ) { + return 'timeout'; + } + } return $result; @@ -105,6 +111,45 @@ function upload( $files, &$results, $force_rewrite = false, $timeout_time = NULL + private function _properties_to_path( $file ) { + $path_pieces = array(); + foreach ( $file->properties as $p ) { + $k = ($p->key == 'path') ? 'path1' : $p->key; + if ( !preg_match( '/^path[0-9]+$/', $k ) ) + continue; + $path_pieces[$k] = $p->value; + } + if ( count( $path_pieces ) == 0 ) + return NULL; + ksort($path_pieces); + return join( $path_pieces ); + } + + + + private function _path_to_properties( $path ) { + // from google drive api docs: + // Maximum of 124 bytes size per property + // (including both key and value) string in UTF-8 encoding. + // Maximum of 30 private properties per file from any one application. + $chunks = str_split( $path, 55 ); + $properties = array(); + $i = 1; + + foreach ( $chunks as $chunk ) { + $p = new \W3TCG_Google_Service_Drive_Property(); + $p->key = 'path' . $i; + $p->value = $chunk; + $properties[] = $p; + + $i++; + } + + return $properties; + } + + + private function _upload_chunk( $files, &$results, $force_rewrite, $timeout_time, $allow_refresh_token ) { list( $result, $listed_files ) = $this->list_files_chunk( $files, @@ -112,45 +157,28 @@ private function _upload_chunk( $files, &$results, $force_rewrite, if ( $result != 'success' ) return $result; - // remove dups - $files_by_title = array(); - - for ( $n = 0; $n < count( $listed_files ); $n++ ) { - $title_to_search = $listed_files[$n]->title; - $files_by_title[$title_to_search] = $listed_files[$n]; - - for ( $m = $n + 1; $m < count( $listed_files ); $m++ ) { - if ( $listed_files[$m]->title == $title_to_search ) { - try { - $this->_service->files->delete( $listed_files[$m]->id ); - } catch ( \W3TCG_Google_Service_Exception $e ) { - $errors = $e->getErrors(); - $details = ''; - if ( count( $errors ) >= 1 ) { - if ( $errors[0]['reason'] == 'notFound' ) { - continue; - } else - $details = $errors[0]['reason']; - } + $files_by_path = array(); - $results[] = $this->_get_result( '', - '', W3TC_CDN_RESULT_ERROR, - 'Failed to delete dup file ' . $title_to_search . ' ' . $details ); - $result = 'with_errors'; - } - } + foreach ( $listed_files as $existing_file ) { + $path = $this->_properties_to_path( $existing_file ); + if ( $path ) { + $files_by_path[$path] = $existing_file; } } // check update date and upload foreach ( $files as $file_descriptor ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - return 'timeout'; + $remote_path = $file_descriptor['remote_path']; + + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) + return 'timeout'; + } list( $parent_id, $title ) = $this->remote_path_to_title( $file_descriptor['remote_path'] ); - $properties = array(); - + $properties = $this->_path_to_properties( $remote_path ); if ( isset( $file_descriptor['content'] ) ) { // when content specified - just upload $content = $file_descriptor['content']; @@ -158,7 +186,7 @@ private function _upload_chunk( $files, &$results, $force_rewrite, $local_path = $file_descriptor['local_path']; if ( !file_exists( $local_path ) ) { $results[] = $this->_get_result( $local_path, - $file_descriptor['remote_path'], + $remote_path, W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file_descriptor ); continue; @@ -171,8 +199,8 @@ private function _upload_chunk( $files, &$results, $force_rewrite, $p->value = $mtime; $properties[] = $p; - if ( !$force_rewrite && isset( $files_by_title[$title] ) ) { - $existing_file = $files_by_title[$title]; + if ( !$force_rewrite && isset( $files_by_path[$remote_path] ) ) { + $existing_file = $files_by_path[$remote_path]; $existing_size = $existing_file->fileSize; $existing_mtime = 0; if ( is_array( $existing_file->properties ) ) { @@ -185,7 +213,7 @@ private function _upload_chunk( $files, &$results, $force_rewrite, $size = @filesize( $local_path ); if ( $mtime == $existing_mtime && $size == $existing_size ) { $results[] = $this->_get_result( $file_descriptor['local_path'], - $file_descriptor['remote_path'], W3TC_CDN_RESULT_OK, + $remote_path, W3TC_CDN_RESULT_OK, 'File up-to-date.', $file_descriptor ); continue; } @@ -205,8 +233,8 @@ private function _upload_chunk( $files, &$results, $force_rewrite, try { try { // update file if there's one already or insert - if ( isset( $files_by_title[$title] ) ) { - $existing_file = $files_by_title[$title]; + if ( isset( $files_by_path[$remote_path] ) ) { + $existing_file = $files_by_path[$remote_path]; $created_file = $this->_service->files->update( $existing_file->id, $file, array( @@ -235,20 +263,21 @@ private function _upload_chunk( $files, &$results, $force_rewrite, } $results[] = $this->_get_result( $file_descriptor['local_path'], - $file_descriptor['remote_path'], W3TC_CDN_RESULT_OK, + $remote_path, W3TC_CDN_RESULT_OK, 'OK', $file_descriptor ); + $this->path_set_id( $remote_path, $created_file->id ); } catch ( \W3TCG_Google_Service_Exception $e ) { $errors = $e->getErrors(); $details = ''; if ( count( $errors ) >= 1 ) { - $details = $errors[0]['reason']; + $details = json_encode($errors); } delete_transient( 'w3tc_cdn_google_drive_folder_ids' ); $results[] = $this->_get_result( $file_descriptor['local_path'], - $file_descriptor['remote_path'], W3TC_CDN_RESULT_ERROR, - 'Failed to upload file ' . $file_descriptor['remote_path'] . + $remote_path, W3TC_CDN_RESULT_ERROR, + 'Failed to upload file ' . $remote_path . ' ' . $details, $file_descriptor ); $result = 'with_errors'; continue; @@ -256,8 +285,8 @@ private function _upload_chunk( $files, &$results, $force_rewrite, delete_transient( 'w3tc_cdn_google_drive_folder_ids' ); $results[] = $this->_get_result( $file_descriptor['local_path'], - $file_descriptor['remote_path'], W3TC_CDN_RESULT_ERROR, - 'Failed to upload file ' . $file_descriptor['remote_path'], + $remote_path, W3TC_CDN_RESULT_ERROR, + 'Failed to upload file ' . $remote_path, $file_descriptor ); $result = 'with_errors'; continue; @@ -516,7 +545,77 @@ function headers_support() { } - function format_url( $path ) { - return $this->_root_url . ltrim( $path, '/' ); + + function purge_all( &$results ) { + return false; + } + + + + private function path_set_id( $path, $id ) { + global $wpdb; + $md5 = md5( $path ); + if ( !$id ) { + $sql = " + INSERT INTO $this->_tablename_pathmap (path, path_hash, remote_id) + VALUES (%s, %s, NULL) + ON DUPLICATE KEY UPDATE remote_id = NULL"; + $wpdb->query($wpdb->prepare($sql, $path, $md5)); + } else { + $sql = " + INSERT INTO $this->_tablename_pathmap (path, path_hash, remote_id) + VALUES (%s, %s, %s) + ON DUPLICATE KEY UPDATE remote_id = %s"; + $wpdb->query($wpdb->prepare( + $sql, $path, $md5, $id, $id)); + } + } + + + + private function path_get_id( $path, $allow_refresh_token = true ) { + global $wpdb; + $md5 = md5($path); + $sql = "SELECT remote_id FROM $this->_tablename_pathmap WHERE path_hash = %s"; + $query = $wpdb->prepare( $sql, $md5 ); + $results = $wpdb->get_results( $query ); + if ( count( $results ) > 0 ) { + return $results[0]->remote_id; + } + $props = $this->_path_to_properties( $path ); + $q = 'trashed = false'; + foreach ( $props as $prop ) { + $key = $prop->key; + $value = str_replace( "'", "\\'", $prop->value ); + $q .= " and properties has { key='$key' and " . + " value='$value' and visibility='PRIVATE' }"; + } + + try { + $items = $this->_service->files->listFiles( + array( 'q' => $q ) ); + } catch ( \W3TCG_Google_Auth_Exception $e ) { + if ( $allow_refresh_token ) { + $this->_refresh_token(); + return $this->path_get_id( $path, false ); + } + + throw $e; + } + + $id = ( count( $items ) == 0 ) ? NULL : $items[0]->id; + $this->path_set_id( $path, $id ); + + return $id; + } + + + + function format_url( $path, $allow_refresh_token = true ) { + $id = $this->path_get_id( Util_Environment::remove_query( $path ) ); + if ( is_null( $id ) ) + return NULL; + + return 'https://drive.google.com/uc?id=' . $id; } } diff --git a/CdnEngine_Mirror_Akamai.php b/CdnEngine_Mirror_Akamai.php index f44325b..5aa5788 100644 --- a/CdnEngine_Mirror_Akamai.php +++ b/CdnEngine_Mirror_Akamai.php @@ -1,10 +1,6 @@ '', + 'alias' => '', + 'consumerkey' => '', + 'consumersecret' => '', + 'zone_id' => 0 + ), $config ); + $split_keys = explode( '+', $config['authorization_key'] ); + if ( sizeof( $split_keys )==3 ) + list( $config['alias'], $config['consumerkey'], $config['consumersecret'] ) = $split_keys; + parent::__construct( $config ); + } + + /** + * Purges remote files + * + * @param array $files + * @param array $results + * @return boolean + */ + function purge( $files, &$results ) { + if ( empty( $this->_config['authorization_key'] ) ) { + $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) ); + + return false; + } + + if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) { + $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) ); + + return false; + } + + if ( !class_exists( 'NetDNA' ) ) { + require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; + } + + $api = new \NetDNA( $this->_config['alias'], + $this->_config['consumerkey'], $this->_config['consumersecret'] ); + $results = array(); + + try { + if ( $this->_config['zone_id'] != 0 ) + $zone_id = $this->_config['zone_id']; + else { + $zone_id = $api->get_zone_id( get_home_url() ); + } + + if ( $zone_id == 0 ) { + $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() ); + } + + if ( $zone_id == 0 ) { + $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) ); + } + + if ( $zone_id == 0 || is_null( $zone_id ) ) { + if ( Util_Environment::home_domain_root_url() == get_home_url() ) + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) ); + else + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) ); + return !$this->_is_error( $results ); + } + + + $files_to_pass = array(); + foreach ( $files as $file ) + $files_to_pass[] = '/' . $file['remote_path']; + $params = array( 'files' => $files_to_pass ); + $file_purge = json_decode( $api->delete( + '/zones/pull.json/' . $zone_id . '/cache', + $params ) ); + + if ( preg_match( "(200|201)", $file_purge->code ) ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' ); + } else { + if ( preg_match( "(401|500)", $file_purge->code ) ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) ); + } else { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code ); + } + } + } catch ( W3tcWpHttpException $e ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() ); + } + + return !$this->_is_error( $results ); + } + + /** + * Purge CDN completely + * + * @param unknown $results + * @return bool + */ + function purge_all( &$results ) { + if ( empty( $this->_config['authorization_key'] ) ) { + $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) ); + + return false; + } + + if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) { + $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) ); + + return false; + } + + if ( !class_exists( 'NetDNA' ) ) { + require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; + } + + $api = new \NetDNA( $this->_config['alias'], $this->_config['consumerkey'], $this->_config['consumersecret'] ); + + $results = array(); + + try { + if ( $this->_config['zone_id'] != 0 ) + $zone_id = $this->_config['zone_id']; + else { + $zone_id = $api->get_zone_id( get_home_url() ); + } + + if ( $zone_id == 0 ) { + $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() ); + } + + + if ( $zone_id == 0 ) { + $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) ); + } + + if ( $zone_id == 0 || is_null( $zone_id ) ) { + if ( Util_Environment::home_domain_root_url() == get_home_url() ) + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) ); + else + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) ); + return !$this->_is_error( $results ); + } + + $file_purge = json_decode( $api->delete( '/zones/pull.json/' . $zone_id . '/cache' ) ); + + if ( preg_match( "(200|201)", $file_purge->code ) ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ) ); + } else { + if ( preg_match( "(401|500)", $file_purge->code ) ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s. Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) ); + } else { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code ); + } + } + + } catch ( W3tcWpHttpException $e ) { + $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() ); + } + + return !$this->_is_error( $results ); + } +} diff --git a/CdnEngine_Mirror_Netdna.php b/CdnEngine_Mirror_Netdna.php deleted file mode 100644 index 3de16aa..0000000 --- a/CdnEngine_Mirror_Netdna.php +++ /dev/null @@ -1,178 +0,0 @@ - '', - 'alias' => '', - 'consumerkey' => '', - 'consumersecret' => '', - 'zone_id' => 0 - ), $config ); - $split_keys = explode( '+', $config['authorization_key'] ); - if ( sizeof( $split_keys )==3 ) - list( $config['alias'], $config['consumerkey'], $config['consumersecret'] ) = $split_keys; - parent::__construct( $config ); - } - - /** - * Purges remote files - * - * @param array $files - * @param array $results - * @return boolean - */ - function purge( $files, &$results ) { - if ( empty( $this->_config['authorization_key'] ) ) { - $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) ); - - return false; - } - - if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) { - $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) ); - - return false; - } - - if ( !class_exists( 'NetDNA' ) ) { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - } - - $api = new \NetDNA( $this->_config['alias'], - $this->_config['consumerkey'], $this->_config['consumersecret'] ); - $results = array(); - - try { - if ( $this->_config['zone_id'] != 0 ) - $zone_id = $this->_config['zone_id']; - else { - $zone_id = $api->get_zone_id( get_home_url() ); - } - - if ( $zone_id == 0 ) { - $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() ); - } - - if ( $zone_id == 0 ) { - $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) ); - } - - if ( $zone_id == 0 || is_null( $zone_id ) ) { - if ( Util_Environment::home_domain_root_url() == get_home_url() ) - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) ); - else - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) ); - return !$this->_is_error( $results ); - } - - - $files_to_pass = array(); - foreach ( $files as $file ) - $files_to_pass[] = '/' . $file['remote_path']; - $params = array( 'files' => $files_to_pass ); - $file_purge = json_decode( $api->delete( - '/zones/pull.json/' . $zone_id . '/cache', - $params ) ); - - if ( preg_match( "(200|201)", $file_purge->code ) ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' ); - } else { - if ( preg_match( "(401|500)", $file_purge->code ) ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) ); - } else { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code ); - } - } - } catch ( W3tcWpHttpException $e ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() ); - } - - return !$this->_is_error( $results ); - } - - /** - * Purge CDN completely - * - * @param unknown $results - * @return bool - */ - function purge_all( &$results ) { - if ( empty( $this->_config['authorization_key'] ) ) { - $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) ); - - return false; - } - - if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) { - $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) ); - - return false; - } - - if ( !class_exists( 'NetDNA' ) ) { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - } - - $api = new \NetDNA( $this->_config['alias'], $this->_config['consumerkey'], $this->_config['consumersecret'] ); - - $results = array(); - - try { - if ( $this->_config['zone_id'] != 0 ) - $zone_id = $this->_config['zone_id']; - else { - $zone_id = $api->get_zone_id( get_home_url() ); - } - - if ( $zone_id == 0 ) { - $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() ); - } - - - if ( $zone_id == 0 ) { - $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) ); - } - - if ( $zone_id == 0 || is_null( $zone_id ) ) { - if ( Util_Environment::home_domain_root_url() == get_home_url() ) - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) ); - else - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) ); - return !$this->_is_error( $results ); - } - - $file_purge = json_decode( $api->delete( '/zones/pull.json/' . $zone_id . '/cache' ) ); - - if ( preg_match( "(200|201)", $file_purge->code ) ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ) ); - } else { - if ( preg_match( "(401|500)", $file_purge->code ) ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s. Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) ); - } else { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code ); - } - } - - } catch ( W3tcWpHttpException $e ) { - $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() ); - } - - return !$this->_is_error( $results ); - } -} diff --git a/CdnEngine_RackSpaceCloudFiles.php b/CdnEngine_RackSpaceCloudFiles.php index 6ff7b53..5e9d8a4 100644 --- a/CdnEngine_RackSpaceCloudFiles.php +++ b/CdnEngine_RackSpaceCloudFiles.php @@ -145,12 +145,16 @@ function _format_url( $path ) { function upload( $files, &$results, $force_rewrite = false, $timeout_time = NULL ) { foreach ( $files as $file ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - break; - $local_path = $file['local_path']; $remote_path = $file['remote_path']; + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) { + return 'timeout'; + } + } + if ( !file_exists( $local_path ) ) { $results[] = $this->_get_result( $local_path, $remote_path, W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file ); diff --git a/CdnEngine_S3.php b/CdnEngine_S3.php index cf7b85e..20a0243 100644 --- a/CdnEngine_S3.php +++ b/CdnEngine_S3.php @@ -30,6 +30,7 @@ function __construct( $config = array() ) { 'key' => '', 'secret' => '', 'bucket' => '', + 'bucket_location' => '', 'cname' => array(), ), $config ); @@ -83,7 +84,19 @@ function _init( &$error ) { return false; } - $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], false ); + if ( empty( $this->_config['bucket_location'] ) ) { + $region = ''; + $endpoint = 's3.amazonaws.com'; + } else { + $region = $this->_config['bucket_location']; + $endpoint = 's3.dualstack.' . $region . '.amazonaws.com'; + } + + $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], + false, $endpoint, $region ); + if ( empty( $region ) ) { + $this->_s3->setSignatureVersion( 'v2' ); + } return true; } @@ -107,12 +120,16 @@ function upload( $files, &$results, $force_rewrite = false, } foreach ( $files as $file ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - break; - $local_path = $file['local_path']; $remote_path = $file['remote_path']; + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) { + return 'timeout'; + } + } + $results[] = $this->_upload( $file, $force_rewrite ); if ( $this->_config['compression'] && $this->_may_gzip( $remote_path ) ) { @@ -167,10 +184,14 @@ function _upload( $file, $force_rewrite = false ) { W3TC_CDN_RESULT_OK, 'OK', $file ); } + if ( strpos( $this->_get_last_error(), 'AWS4-HMAC-SHA256' ) !== false ) { + $error = "Bucket location region is incorrect. Please select the right one."; + } else { + $error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() ); + } + return $this->_get_result( $local_path, $remote_path, - W3TC_CDN_RESULT_ERROR, - sprintf( 'Unable to put object (%s).', $this->_get_last_error() ), - $file ); + W3TC_CDN_RESULT_ERROR, $error, $file ); } /** @@ -235,10 +256,14 @@ function _upload_gzip( $file, $force_rewrite = false ) { W3TC_CDN_RESULT_OK, 'OK', $file ); } + if ( strpos( $this->_get_last_error(), 'AWS4-HMAC-SHA256' ) !== false ) { + $error = "Bucket location region is incorrect. Please select the right one."; + } else { + $error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() ); + } + return $this->_get_result( $local_path, $remote_path, - W3TC_CDN_RESULT_ERROR, - sprintf( 'Unable to put object (%s).', $this->_get_last_error() ), - $file ); + W3TC_CDN_RESULT_ERROR, $error, $file ); } /** @@ -337,7 +362,11 @@ function test( &$error ) { } if ( !@$this->_s3->putObjectString( $string, $this->_config['bucket'], $string, \S3::ACL_PUBLIC_READ ) ) { - $error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() ); + if ( strpos( $this->_get_last_error(), 'AWS4-HMAC-SHA256' ) !== false ) { + $error = "Bucket location region is incorrect. Please select the right one."; + } else { + $error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() ); + } $this->_restore_error_handler(); diff --git a/CdnEngine_S3_Cf.php b/CdnEngine_S3_Cf.php index 1cbb256..c104e84 100644 --- a/CdnEngine_S3_Cf.php +++ b/CdnEngine_S3_Cf.php @@ -67,7 +67,20 @@ function _init( &$error ) { return false; } - $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], false ); + if ( empty( $this->_config['bucket_location'] ) ) { + $region = ''; + $endpoint = 's3.amazonaws.com'; + } else { + $region = $this->_config['bucket_location']; + $endpoint = 's3.dualstack.'.$region.'.amazonaws.com'; + } + + $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], + false, $endpoint, $region ); + + if ( empty( $region ) ) { + $this->_s3->setSignatureVersion( 'v2' ); + } return true; } @@ -193,7 +206,7 @@ function invalidate( $files, &$results ) { } $this->_set_error_handler(); - $invalidation = @$this->_s3->createInvalidation( $dist['id'], $paths ); + $invalidation = @$this->_s3->invalidateDistribution( $dist['id'], $paths ); $this->_restore_error_handler(); if ( !$invalidation ) { @@ -358,7 +371,7 @@ function create_container( &$container_id, &$error ) { $matches = null; - if ( preg_match( '~^(.+)\.cloudfront\.net$~', $dist['domain'], $matches ) ) { + if ( !empty( $dist['domain'] ) && preg_match( '~^(.+)\.cloudfront\.net$~', $dist['domain'], $matches ) ) { $container_id = $matches[1]; } diff --git a/CdnEngine_S3_Compatible.php b/CdnEngine_S3_Compatible.php index 939645f..580976c 100644 --- a/CdnEngine_S3_Compatible.php +++ b/CdnEngine_S3_Compatible.php @@ -33,6 +33,8 @@ function __construct( $config = array() ) { $this->_s3 = new \S3( $config['key'], $config['secret'], false, $config['api_host'] ); + $this->_s3->setSignatureVersion( 'v2' ); + parent::__construct( $config ); } @@ -72,12 +74,16 @@ function upload( $files, &$results, $force_rewrite = false, $error = null; foreach ( $files as $file ) { - if ( !is_null( $timeout_time ) && time() > $timeout_time ) - break; - $local_path = $file['local_path']; $remote_path = $file['remote_path']; + // process at least one item before timeout so that progress goes on + if ( !empty( $results ) ) { + if ( !is_null( $timeout_time ) && time() > $timeout_time ) { + return 'timeout'; + } + } + $results[] = $this->_upload( $file, $force_rewrite ); if ( $this->_config['compression'] && $this->_may_gzip( $remote_path ) ) { diff --git a/Cdn_AdminActions.php b/Cdn_AdminActions.php index 5a46c98..3a752c1 100644 --- a/Cdn_AdminActions.php +++ b/Cdn_AdminActions.php @@ -118,12 +118,12 @@ function w3tc_cdn_export_library_process() { $results = array(); $w3_plugin_cdn->export_library( $limit, $offset, $count, $total, - $results, time() + 5 ); + $results, time() + 120 ); $response = array( 'limit' => $limit, 'offset' => $offset, - 'count' => count( $results ), + 'count' => $count, 'total' => $total, 'results' => $results ); @@ -493,265 +493,26 @@ function w3tc_cdn_s3_bucket_location() { $type = Util_Request::get_string( 'type', 's3' ); $locations = array( - '' => 'US (Default)', - 'us-west-1' => __( 'US-West (Northern California)', 'w3-total-cache' ), - 'EU' => 'Europe', - 'ap-southeast-1' => __( 'AP-SouthEast (Singapore)', 'w3-total-cache' ), + 'us-east-1' => __( 'US East (N. Virginia)', 'w3-total-cache' ), + 'us-east-2' => __( 'US East (Ohio)', 'w3-total-cache' ), + 'us-west-1' => __( 'US-West (N. California)', 'w3-total-cache' ), + 'us-west-2' => __( 'US-West (Oregon)', 'w3-total-cache' ), + 'ca-central-1' => __( 'Canada (Montreal)', 'w3-total-cache' ), + 'ap-south-1' => __( 'Asia Pacific (Mumbai)', 'w3-total-cache' ), + 'ap-northeast-2'=> __( 'Asia Pacific (Seoul)', 'w3-total-cache' ), + 'ap-southeast-1'=> __( 'Asia Pacific (Singapore)', 'w3-total-cache' ), + 'ap-southeast-2'=> __( 'Asia Pacific (Sydney)', 'w3-total-cache' ), + 'ap-northeast-1'=> __( 'Asia Pacific (Tokyo)', 'w3-total-cache' ), + 'eu-central-1' => __( 'EU (Frankfurt)', 'w3-total-cache' ), + 'eu-west-1' => __( 'EU (Ireland)', 'w3-total-cache' ), + 'eu-west-2' => __( 'EU (London)', 'w3-total-cache' ), + 'sa-east-1' => __( 'South America (São Paulo)', 'w3-total-cache' ), ); include W3TC_INC_DIR . '/lightbox/cdn_s3_bucket_location.php'; } - /** - * Includes the manual create pull zone form. - */ - function w3tc_cdn_create_netdna_maxcdn_pull_zone_form() { - $type = Util_Request::get_string( 'type', 'maxcdn' ); - include W3TC_INC_DIR . '/lightbox/create_netdna_maxcdn_pull_zone.php'; - } - - - /** - * Create NetDNA/MaxCDN pullzone - */ - function w3tc_cdn_create_netdna_maxcdn_pull_zone() { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - $type = Util_Request::get_string( 'type' ); - $name = Util_Request::get_string( 'name' ); - $label = Util_Request::get_string( 'label' ); - $cdn_engine= $type; - - $authorization_key = $this->_config->get_string( "cdn.$cdn_engine.authorization_key" ); - $alias = $consumerkey = $consumersecret = ''; - - if ( $authorization_key ) { - $keys = explode( '+', $authorization_key ); - if ( sizeof( $keys ) == 3 ) { - list( $alias, $consumerkey, $consumersecret ) = $keys; - } - } - $api = new \NetDNA( $alias, $consumerkey, $consumersecret ); - $url = get_home_url(); - $zone = array(); - $zone['name'] = $name; - $zone['label'] = $label; - $zone['url'] = $url; - $zone['use_stale'] = 1; - $zone['queries'] = 1; - $zone['compress'] = 1; - $zone['backend_compress'] = 1; - try { - $response = $api->create_pull_zone( $zone ); - try { - $temporary_url = "$name.$alias.netdna-cdn.com"; - $test_result = -1; - if ( !$this->_config->get_array( "cdn.$cdn_engine.domain" ) ) { - $test_result = $this->test_cdn_url( $temporary_url ) ? 1 : 0; - $this->_config->set( "cdn.$cdn_engine.domain", array( $temporary_url ) ); - if ( $test_result ) - $this->_config->set( "cdn.enabled", true ); - } - $this->_config->save(); - $state = Dispatcher::config_state(); - $zones = $api->get_pull_zones(); - $zone_count = sizeof( $zones ); - Util_Admin::make_track_call( array( 'type'=>'cdn', - 'data'=>array( - 'cdn' => $type, 'action' => 'zonecreation', 'creation' => 'manual', 'creationtime' => time() - , 'signupclick' => $state->get_integer( 'track.maxcdn_signup' ) - , 'authorizeclick' => $state->get_integer( 'track.maxcdn_authorize' ) - , 'validationclick' => $state->get_integer( 'track.maxcdn_validation' ) - , 'total_zones' => $zone_count - , 'test' => $test_result - ) ) ); - } catch ( \Exception $ex ) {} - echo json_encode( array( 'status' => 'success', 'message' => 'Pull Zone created.', 'temporary_url' => "$name.$alias.netdna-cdn.com", 'data' => $response ) ); - } catch ( \Exception $ex ) { - echo json_encode( array( 'status' => 'error', 'message' => $ex->getMessage() ) ); - } - } - - /** - * Validates the authorization key and echos json encoded data connected with the key. - */ - function w3tc_cdn_validate_authorization_key() { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - - $cdn_engine = Util_Request::get_string( 'type' ); - $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine ); - $authorization_key = Util_Request::get_string( 'authorization_key' ); - $this->validate_authorization_key( $authorization_key ); - $keys = explode( '+', $authorization_key ); - list( $alias, $consumer_key, $consumer_secret ) = $keys; - $api = new \NetDNA( $alias, $consumer_key, $consumer_secret ); - $this->validate_account( $api ); - try { - $pull_zones = $api->get_zones_by_url( get_home_url() ); - if ( sizeof( $pull_zones ) == 0 ) { - $result = array( 'result' => 'create' ); - try { - $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key ); - $this->_config->save(); - } catch ( \Exception $ex ) {} - } elseif ( sizeof( $pull_zones ) == 1 ) { - $custom_domains = $api->get_custom_domains( $pull_zones[0]['id'] ); - if ( sizeof( $custom_domains ) > 0 ) { - $result = array( 'result' => 'single', 'cnames' => array( $custom_domains ) ); - $this->_config->set( "cdn.$cdn_engine.domain", $custom_domains ); - $this->_config->set( "cdn.enabled", true ); - } else { - $name = $pull_zones[0]['name']; - $result = array( 'result' => 'single', 'cnames' => array( "$name.$alias.netdna-cdn.com" ) ); - } - $this->_config->set( "cdn.$cdn_engine.zone_id", $pull_zones[0]['id'] ); - $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key ); - $this->_config->set( "cdn.$cdn_engine.domain", $result['cnames'] ); - $this->_config->save(); - } else { - $zones = array(); - $data = array(); - foreach ( $pull_zones as $zone ) { - if ( empty( $data ) ) { - $domains = $this->test_cdn_pull_zone( $api, $zone['id'], $zone['name'], $alias ); - if ( $domains ) { - $data = array( 'id' => $zone['id'], 'domains' => $domains ); - $this->_config->set( "cdn.$cdn_engine.zone_id", $zone['id'] ); - $this->_config->set( "cdn.$cdn_engine.domain", $domains ); - } - } - $zones[] = array( 'id' => $zone['id'], 'name' => $zone['name'] ); - } - $result = array( 'result' => 'many', 'zones' => $zones, 'data' => $data ); - $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key ); - $this->_config->save(); - } - try { - $state = Dispatcher::config_state(); - if ( $state->get_integer( 'track.maxcdn_validation', 0 ) == 0 ) { - $state->set( 'track.maxcdn_validation', time() ); - $state->save(); - } - } catch ( \Exception $ex ) {} - } catch ( \Exception $ex ) { - $result = array( 'result' => 'error', 'message' => $ex->getMessage() ); - } - echo json_encode( $result ); - exit(); - } - - /** - * - * - * @param NetDNA $api - * @param int $id - * @param string $name - * @param string $alias - * @return array|null - */ - private function test_cdn_pull_zone( $api, $id, $name, $alias ) { - try { - $domains = $api->get_custom_domains( $id ); - if ( $domains ) { - $test = true; - foreach ( $domains as $domain ) - $test = $test && $this->test_cdn_url( $domain ); - } else { - $url = "$name.$alias.netdna-cdn.com"; - $test = $this->test_cdn_url( $url ); - $domains = array( $url ); - } - if ( $test ) - return $domains; - } catch ( \Exception $ex ) {} - return array(); - } - /** - * Validates key and echos encoded message on failure. - * - * @param unknown $authorization_key - */ - private function validate_authorization_key( $authorization_key ) { - if ( empty( $authorization_key ) ) { - $result = array( 'result' => 'error', 'message' => __( 'An authorization key was not provided.', 'w3-total-cache' ) ); - echo json_encode( $result ); - exit(); - } - $keys = explode( '+', $authorization_key ); - if ( sizeof( $keys ) != 3 ) { - $result = array( 'result' => 'error', 'message' => sprintf( __( 'The provided authorization key is - not in the correct format: %s.', 'w3-total-cache' ), $authorization_key ) ); - echo json_encode( $result ); - exit(); - - } - } - - /** - * Validates that the API works and echos message and exists if it fails - * - * @param NetDNA $api - * @return null|array - */ - private function validate_account( $api ) { - try { - return $api->get_account(); - } catch ( \Exception $ex ) { - $result = array( 'result' => 'error', 'message' => $ex->getMessage() ); - echo json_encode( $result ); - exit(); - } - } - - /** - * Create a NetDNA or MaxCDN pull zone automatically - */ - function w3tc_cdn_auto_create_netdna_maxcdn_pull_zone() { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - - $cdn_engine = Util_Request::get_string( 'type' ); - $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine ); - $authorization_key = Util_Request::get_string( 'authorization_key' ); - $this->validate_authorization_key( $authorization_key ); - $keys = explode( '+', $authorization_key ); - list( $alias, $consumerkey, $consumersecret ) = $keys; - $url = get_home_url(); - try { - $api = new \NetDNA( $alias, $consumerkey, $consumersecret ); - $disable_cooker_header = $this->_config->get_boolean( 'browsercache.other.nocookies' ) || - $this->_config->get_boolean( 'browsercache.cssjs.nocookies' ); - $zone = $api->create_default_pull_zone( $url, null, null, - array( 'ignore_setcookie_header' => $disable_cooker_header ) ); - $name = $zone['name']; - $temporary_url = "$name.$alias.netdna-cdn.com"; - $test_result = -1; - if ( !$this->_config->get_array( "cdn.$cdn_engine.domain" ) ) { - $test_result = $this->test_cdn_url( $temporary_url ) ? 1 : 0; - $this->_config->set( "cdn.$cdn_engine.zone_id", $zone['id'] ); - if ( $test_result ) - $this->_config->set( "cdn.enabled", true ); - $this->_config->set( "cdn.$cdn_engine.domain", array( $temporary_url ) ); - } - $this->_config->save(); - $state = Dispatcher::config_state(); - $zones = $api->get_pull_zones(); - $zone_count = sizeof( $zones ); - Util_Admin::make_track_call( array( 'type'=>'cdn', - 'data'=>array( - 'cdn' => $cdn_engine, 'action' => 'zonecreation' - , 'creation' => 'manual', 'creationtime' => time() - , 'signupclick' => $state->get_integer( 'track.maxcdn_signup' ) - , 'authorizeclick' => $state->get_integer( 'track.maxcdn_authorize' ) - , 'validationclick' => $state->get_integer( 'track.maxcdn_validation' ) - , 'total_zones' => $zone_count - , 'test' => $test_result ) ) ); - $result = array( 'result' => 'single', 'cnames' => array( $temporary_url ) ); - } catch ( \Exception $ex ) { - $result = array( 'result' => 'error', 'message' => sprintf( __( 'Could not create default zone.' . $ex->getMessage(), 'w3-total-cache' ) ) ); - } - echo json_encode( $result ); - exit(); - } private function test_cdn_url( $url ) { $response = wp_remote_get( $url ); @@ -762,70 +523,8 @@ private function test_cdn_url( $url ) { return 200 == $code; } } - /** - * Configures the plugin to use the zone id provided in request - */ - function w3tc_cdn_use_netdna_maxcdn_pull_zone() { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - - $cdn_engine = Util_Request::get_string( 'type' ); - $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine ); - $authorization_key = Util_Request::get_string( 'authorization_key' ); - $this->validate_authorization_key( $authorization_key ); - $zone_id = Util_Request::get_integer( 'zone_id' ); - $keys = explode( '+', $authorization_key ); - list( $alias, $consumer_key, $consumer_secret ) = $keys; - $api = new \NetDNA( $alias, $consumer_key, $consumer_secret ); - $this->validate_account( $api ); - try { - $pull_zone = $api->get_zone( $zone_id ); - if ( $pull_zone ) { - $custom_domains = $api->get_custom_domains( $pull_zone['id'] ); - if ( sizeof( $custom_domains ) > 0 ) { - $result = array( 'result' => 'valid', 'cnames' => array( $custom_domains ) ); - $test = true; - foreach ( $custom_domains as $url ) - $test = $test && $this->test_cdn_url( $url ); - if ( $test ) - $this->_config->set( "cdn.enabled", true ); - } else { - $name = $pull_zone['name']; - $result = array( 'result' => 'valid', 'cnames' => array( "$name.$alias.netdna-cdn.com" ) ); - $test = $this->test_cdn_url( "$name.$alias.netdna-cdn.com" ); - if ( $test ) - $this->_config->set( "cdn.enabled", true ); - } - $this->_config->set( "cdn.enabled", true ); - $this->_config->set( "cdn.$cdn_engine.zone_id", $pull_zone['id'] ); - $this->_config->set( "cdn.$cdn_engine.domain", $result['cnames'] ); - $this->_config->save(); - } else { - $result = array( 'result' => 'error', 'message' => sprintf( __( 'The provided zone id was not connected to the provided authorization key.', 'w3-total-cache' ) ) ); - } - } catch ( \Exception $ex ) { - $result = array( 'result' => 'error', 'message' => $ex->getMessage() ); - } - echo json_encode( $result ); - exit(); - } - function w3tc_cdn_save_activate() { - try { - $this->_config->set( 'cdn.enabled', true ); - $this->_config->save(); - } catch ( \Exception $ex ) {} - Util_Environment::redirect( Util_Ui::admin_url( sprintf( 'admin.php?page=w3tc_cdn#configuration' ) ) ); - } - - function w3tc_cdn_maxcdn_signup() { - try { - $state = Dispatcher::config_state(); - $state->set( 'track.maxcdn_signup', time() ); - $state->save(); - } catch ( \Exception $ex ) {} - Util_Environment::redirect( MAXCDN_SIGNUP_URL ); - } function w3tc_cdn_maxcdn_authorize() { try { @@ -838,28 +537,12 @@ function w3tc_cdn_maxcdn_authorize() { Util_Environment::redirect( MAXCDN_AUTHORIZE_URL ); } - function w3tc_cdn_netdna_authorize() { + function w3tc_cdn_maxcdn_signup() { try { $state = Dispatcher::config_state(); - if ( $state->get_integer( 'track.maxcdn_authorize', 0 ) == 0 ) { - $state->set( 'track.maxcdn_authorize', time() ); - $state->save(); - } + $state->set( 'track.maxcdn_signup', time() ); + $state->save(); } catch ( \Exception $ex ) {} - Util_Environment::redirect( NETDNA_AUTHORIZE_URL ); - } - - /** - * Validates cdn engine and echos message and exists if it fails - * - * @param unknown $cdn_engine - */ - private function validate_cdnengine_is_netdna_maxcdn( $cdn_engine ) { - if ( !in_array( $cdn_engine, array( 'netdna', 'maxcdn' ) ) ) { - $result = array( 'result' => 'notsupported', 'message' => sprintf( __( '%s is not supported for Pull Zone - selection.', 'w3-total-cache' ), $cdn_engine ) ); - echo json_encode( $result ); - exit(); - } + Util_Environment::redirect( MAXCDN_SIGNUP_URL ); } } diff --git a/Cdn_AdminNotes.php b/Cdn_AdminNotes.php index 7a3c684..6233154 100644 --- a/Cdn_AdminNotes.php +++ b/Cdn_AdminNotes.php @@ -108,7 +108,7 @@ public function w3tc_notes( $notes ) { } } - if ( in_array( $config->get_string( 'cdn.engine' ), array( 'netdna', 'maxcdn' ) ) && + if ( $config->get_string( 'cdn.engine' ) == 'maxcdn' && !$state->get_boolean( 'cdn.hide_note_maxcdn_whitelist_ip' ) && $state->get_integer( 'track.maxcdn_authorize' ) == 0 && $config->get_string( 'cdn.' . $config->get_string( 'cdn.engine' ) .'.authorization_key' ) ) { @@ -233,26 +233,6 @@ function w3tc_errors( $errors ) { $error = __( 'The "Replace default hostname with" field cannot be empty.', 'w3-total-cache' ); break; - case ( $cdn_engine == 'netdna' ): - $fields = array(); - if ( $c->get_string( 'cdn.netdna.authorization_key' ) == '' ) - $fields[] = '"' . __( 'Authorization key', 'w3-total-cache' ) . '"'; - - if ( !count( $c->get_array( 'cdn.netdna.domain' ) ) ) - $fields[] = '"' . __( 'Replace default hostname with', 'w3-total-cache' ) . '"'; - - if ( $fields ) { - $error = sprintf( __( 'The %s field(s) cannot be empty.', 'w3-total-cache' ), - implode( __( ' and ', 'w3-total-cache' ), $fields ) ); - } - - if ( $c->get_string( 'cdn.netdna.authorization_key' ) != '' && - sizeof( explode( '+', $c->get_string( 'cdn.netdna.authorization_key' ) ) ) != 3 ) - $error .= __( 'The "Authorization key" is not correct.', 'w3-total-cache' ); - elseif ( $c->get_integer( 'cdn.netdna.zone_id', 0 ) <= 0 ) - $error .= __( 'You need to select / create a pull zone.', 'w3-total-cache' ); - break; - case ( $cdn_engine == 'maxcdn' ): $fields = array(); if ( $c->get_string( 'cdn.maxcdn.authorization_key' ) == '' ) diff --git a/Cdn_CloudFrontFsd_Page.php b/Cdn_CloudFrontFsd_Page.php deleted file mode 100644 index 35f5414..0000000 --- a/Cdn_CloudFrontFsd_Page.php +++ /dev/null @@ -1,18 +0,0 @@ - __( 'CDN:', 'w3-total-cache' ), 'cdn.engine' => __( 'CDN Type:', 'w3-total-cache' ), 'cdn.debug' => __( 'CDN', 'w3-total-cache' ), + 'cdnfsd.debug' => __( 'FSD CDN', 'w3-total-cache' ), 'cdn.uploads.enable' => __( 'Host attachments', 'w3-total-cache' ), 'cdn.includes.enable' => __( 'Host wp-includes/ files', 'w3-total-cache' ), 'cdn.theme.enable' => __( 'Host theme files', 'w3-total-cache' ), @@ -16,6 +17,7 @@ public function config_labels( $config_labels ) { 'cdn.import.external' => __( 'Import external media library attachments', 'w3-total-cache' ), 'cdn.canonical_header' => __( 'Add canonical header', 'w3-total-cache' ), 'cdn.reject.ssl' => __( 'Disable CDN on SSL pages', 'w3-total-cache' ), + 'cdn.admin.media_library' => __( 'Use CDN links for the Media Library on admin pages', 'w3-total-cache' ), 'cdn.reject.logged_roles' => __( 'Disable CDN for the following roles', 'w3-total-cache' ), 'cdn.reject.uri' => __( 'Disable CDN on the following pages:', 'w3-total-cache' ), 'cdn.autoupload.enabled' => __( 'Export changed files automatically', 'w3-total-cache' ), diff --git a/Cdn_Core.php b/Cdn_Core.php index aebaacd..95e236e 100644 --- a/Cdn_Core.php +++ b/Cdn_Core.php @@ -367,6 +367,7 @@ function get_cdn() { 'key' => $this->_config->get_string( 'cdn.cf.key' ), 'secret' => $this->_config->get_string( 'cdn.cf.secret' ), 'bucket' => $this->_config->get_string( 'cdn.cf.bucket' ), + 'bucket_location' => $this->_config->get_string( 'cdn.cf.bucket.location' ), 'id' => $this->_config->get_string( 'cdn.cf.id' ), 'cname' => $this->_config->get_array( 'cdn.cf.cname' ), 'ssl' => $this->_config->get_string( 'cdn.cf.ssl' ), @@ -477,16 +478,6 @@ function get_cdn() { ); break; - case 'netdna': - $engine_config = array( - 'authorization_key' => $this->_config->get_string( 'cdn.netdna.authorization_key' ), - 'zone_id' => $this->_config->get_integer( 'cdn.netdna.zone_id' ), - 'domain' => $this->_config->get_array( 'cdn.netdna.domain' ), - 'ssl' => $this->_config->get_string( 'cdn.netdna.ssl' ), - 'compression' => false - ); - break; - case 'rackspace_cdn': $state = Dispatcher::config_state(); @@ -533,6 +524,7 @@ function get_cdn() { 'key' => $this->_config->get_string( 'cdn.s3.key' ), 'secret' => $this->_config->get_string( 'cdn.s3.secret' ), 'bucket' => $this->_config->get_string( 'cdn.s3.bucket' ), + 'bucket_location' => $this->_config->get_string( 'cdn.s3.bucket.location' ), 'cname' => $this->_config->get_array( 'cdn.s3.cname' ), 'ssl' => $this->_config->get_string( 'cdn.s3.ssl' ), 'compression' => $compression @@ -669,6 +661,25 @@ function uri_to_cdn_uri( $local_uri ) { return ltrim( $remote_uri, '/' ); } + /** + * Need to pass full URL and it's URI + * URI passed to prevent redundant parsing, normally it's available for caller + **/ + function url_to_cdn_url( $url, $path ) { + $cdn = $this->get_cdn(); + $remote_path = $this->uri_to_cdn_uri( $path ); + $new_url = $cdn->format_url( $remote_path ); + if ( !$new_url ) { + return null; + } + $is_engine_mirror = Cdn_Util::is_engine_mirror( + $this->_config->get_string( 'cdn.engine' ) ); + + $new_url = apply_filters( 'w3tc_cdn_url', $new_url, $url, + $is_engine_mirror ); + return $new_url; + } + /** * Returns the sitepath for multisite subfolder or subdomain path for multisite subdomain * diff --git a/Cdn_Core_Admin.php b/Cdn_Core_Admin.php index 566965e..39873e7 100644 --- a/Cdn_Core_Admin.php +++ b/Cdn_Core_Admin.php @@ -209,7 +209,9 @@ function export_library( $limit, $offset, &$count, &$total, &$results, $timeout_ WHERE p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL) GROUP BY - p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix ); + p.ID + ORDER BY + p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix ); if ( $limit ) { $sql .= sprintf( ' LIMIT %d', $limit ); @@ -715,10 +717,12 @@ function media_row_actions( $actions, $post ) { } /** - * Changes settings on MaxCDN/NetDNA site + * Changes settings on MaxCDN site */ function change_canonical_header() { - if ( in_array( $cdn_engine = $this->_config->get_string( 'cdn.engine' ), array( 'maxcdn', 'netdna' ) ) ) { + $cdn_engine = $this->_config->get_string( 'cdn.engine' ); + + if ( $cdn_engine == 'maxcdn' ) { require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; $authorization_key = $this->_config->get_string( "cdn.$cdn_engine.authorization_key" ); if ( $authorization_key ) { @@ -776,10 +780,6 @@ function is_running() { $running = false; break; - case ( $cdn_engine == 'netdna' ): - $running = false; - break; - case ( $cdn_engine == 'maxcdn' ): $running = false; break; diff --git a/Cdn_Environment.php b/Cdn_Environment.php index f5f2951..d2b1716 100644 --- a/Cdn_Environment.php +++ b/Cdn_Environment.php @@ -1,11 +1,6 @@ get_boolean( 'cdn.enabled' ) ) { try { - $this->table_create( $event == 'activate' ); + $this->handle_tables( + $event == 'activate' /* drop state on activation */, + true ); } catch ( \Exception $ex ) { $exs->push( $ex ); } @@ -96,7 +93,7 @@ public function fix_after_deactivation() { $exs = new Util_Environment_Exceptions(); $this->rules_remove( $exs ); - $this->table_delete(); + $this->handle_tables( true, false ); if ( count( $exs->exceptions() ) > 0 ) throw $exs; @@ -162,33 +159,36 @@ public function rules_generate_for_ftp( $config ) { - /* - * table operations - */ - /** - * Create queue table + * Create tables * * @param bool $drop * @throws Util_Environment_Exception */ - private function table_create( $drop = false ) { + private function handle_tables( $drop, $create ) { global $wpdb; - if ( $drop ) { - $sql = sprintf( 'DROP TABLE IF EXISTS `%s%s`;', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE ); + $tablename_queue = $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE; + $tablename_map = $wpdb->base_prefix . W3TC_CDN_TABLE_PATHMAP; + if ( $drop ) { + $sql = "DROP TABLE IF EXISTS `$tablename_queue`;"; + $wpdb->query( $sql ); + $sql = "DROP TABLE IF EXISTS `$tablename_map`;"; $wpdb->query( $sql ); } - $charset_collate = ''; + if ( !$create ) { + return; + } + $charset_collate = ''; if ( ! empty( $wpdb->charset ) ) $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; if ( ! empty( $wpdb->collate ) ) $charset_collate .= " COLLATE $wpdb->collate"; - $sql = sprintf( "CREATE TABLE IF NOT EXISTS `%s%s` ( + $sql = "CREATE TABLE IF NOT EXISTS `$tablename_queue` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `local_path` varchar(500) NOT NULL DEFAULT '', `remote_path` varchar(500) NOT NULL DEFAULT '', @@ -197,26 +197,34 @@ private function table_create( $drop = false ) { `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `date` (`date`) - ) $charset_collate;", $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE ); + ) $charset_collate;"; $wpdb->query( $sql ); + if ( !$wpdb->result ) + throw new Util_Environment_Exception( 'Can\'t create table ' . + $tablename_queue ); + + $sql = " + CREATE TABLE IF NOT EXISTS `$tablename_map` ( + -- Relative file path. + -- For reference, not actually used for finding files. + path TEXT NOT NULL, + -- MD5 hash of remote path, used for finding files. + path_hash VARCHAR(32) CHARACTER SET ascii NOT NULL, + type tinyint(1) NOT NULL DEFAULT '0', + -- Google Drive: document identifier + remote_id VARCHAR(200) CHARACTER SET ascii, + PRIMARY KEY (path_hash), + KEY `remote_id` (`remote_id`) + ) $charset_collate"; + $wpdb->query( $sql ); if ( !$wpdb->result ) throw new Util_Environment_Exception( 'Can\'t create table ' . - $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE ); + $tablename_map ); } - /** - * Delete queue table - * - * @return void - */ - private function table_delete() { - global $wpdb; - $sql = sprintf( 'DROP TABLE IF EXISTS `%s%s`', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE ); - $wpdb->query( $sql ); - } private function generate_table_sql() { global $wpdb; @@ -317,9 +325,12 @@ private function rules_remove( $exs ) { private function rules_generate( $config, $cdnftp = false ) { $rules = ''; if ( Dispatcher::canonical_generated_by( $config, $cdnftp ) == 'cdn' ) - $rules .= Util_RuleSnippet::canonical( $config, $cdnftp ); - if ( Dispatcher::allow_origin_generated_by( $config ) == 'cdn' ) - $rules .= Util_RuleSnippet::allow_origin( $config, $cdnftp ); + $rules .= Util_RuleSnippet::canonical( $cdnftp, + $config->get_boolean( 'cdn.cors_header') ); + + if ( $config->get_boolean( 'cdn.cors_header') ) { + $rules .= $this->allow_origin( $cdnftp ); + } if ( strlen( $rules ) > 0 ) $rules = @@ -329,4 +340,27 @@ private function rules_generate( $config, $cdnftp = false ) { return $rules; } + + /** + * Returns allow-origin rules + */ + private function allow_origin( $cdnftp = false ) { + switch ( true ) { + case Util_Environment::is_apache(): + case Util_Environment::is_litespeed(): + $r = "\n"; + $r .= " Header set Access-Control-Allow-Origin \"*\"\n"; + $r .= "\n"; + + if ( !$cdnftp ) + return $r; + else + return + "\n" . + $r . + "\n"; + } + + return ''; + } } diff --git a/Cdn_Fsd_Core.php b/Cdn_Fsd_Core.php deleted file mode 100644 index db41962..0000000 --- a/Cdn_Fsd_Core.php +++ /dev/null @@ -1,41 +0,0 @@ -get_string( 'cdn.engine' ); - - switch ( $engine ) { - case 'cloudfront_fsd': - $engine_object = new Cdn_CloudFrontFsd_Engine( array( - 'access_key' => $c->get_string( 'cdn.cloudfront_fsd.access_key' ), - 'secret_key' => $c->get_string( 'cdn.cloudfront_fsd.secret_key' ), - 'distribution_id' => $c->get_string( 'cdn.cloudfront_fsd.distribution_id' ) - ) ); - break; - - case 'maxcdn_fsd': - $engine_object = new Cdn_MaxCdnFsd_Engine( array( - 'api_key' => $c->get_string( 'cdn.maxcdn_fsd.api_key' ), - 'zone_id' => $c->get_integer( 'cdn.maxcdn_fsd.zone_id' ) - ) ); - break; - - default: - throw new \Exception( 'unknown engine ' . $engine ); - } - } - - return $engine_object; - } -} diff --git a/Cdn_GeneralPage_View.php b/Cdn_GeneralPage_View.php index 5a04c46..b89f80c 100644 --- a/Cdn_GeneralPage_View.php +++ b/Cdn_GeneralPage_View.php @@ -30,12 +30,14 @@ 'selectbox_values' => $engine_values, 'selectbox_optgroups' => $engine_optgroups, 'description' => __( 'Select the CDN type you wish to use.', - 'w3-total-cache' ) . $cdn_engine_extra_description + 'w3-total-cache' ) ) ); ?> - + cds.get_string( 'cdn.highwinds.host.hash_code' ) ?>.hwcdn.net @@ -34,7 +34,7 @@ -
CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> @@ -44,7 +44,7 @@
- CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> + CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> diff --git a/Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php b/Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php index be070f8..5cba207 100644 --- a/Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php +++ b/Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php @@ -13,7 +13,7 @@
- CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> + CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>

get_string( 'cdn.maxcdn.authorization_key' ); + $zone = $config->get_string( 'cdn.maxcdn.zone_id' ); + $domains = $config->get_array( 'cdn.maxcdn.domain' ); + + $authorized = !empty( $key ) && !empty( $zone ); + $http_domain = isset( $domains['http_default'] ) ? $domains['http_default'] : null; + $https_domain = isset( $domains['https_default'] ) ? $domains['https_default'] : null; + + include W3TC_DIR . '/Cdn_MaxCdn_Page_View.php'; + } +} diff --git a/Cdn_MaxCdn_Page_View.js b/Cdn_MaxCdn_Page_View.js new file mode 100644 index 0000000..cd7c901 --- /dev/null +++ b/Cdn_MaxCdn_Page_View.js @@ -0,0 +1,68 @@ +jQuery(function($) { + function w3tc_maxcdn_resize(o) { + o.options.height = jQuery('.w3tc_cdn_maxcdn_form').height(); + o.resize(); + } + + $('body') + .on('click', '.w3tc_cdn_maxcdn_authorize', function() { + W3tc_Lightbox.open({ + id:'w3tc-overlay', + close: '', + width: 800, + height: 300, + url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=cdn_maxcdn_intro', + callback: w3tc_maxcdn_resize + }); + }) + + + + .on('click', '.w3tc_cdn_maxcdn_list_zones', function() { + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=cdn_maxcdn_list_zones'; + + W3tc_Lightbox.load_form(url, '.w3tc_cdn_maxcdn_form', w3tc_maxcdn_resize); + }) + + + + .on('click', '.w3tc_cdn_maxcdn_view_zone', function() { + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=cdn_maxcdn_view_zone'; + + W3tc_Lightbox.load_form(url, '.w3tc_cdn_maxcdn_form', w3tc_maxcdn_resize); + }) + + + + .on('click', '.w3tc_cdn_maxcdn_configure_zone', function() { + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=cdn_maxcdn_configure_zone'; + + W3tc_Lightbox.load_form(url, '.w3tc_cdn_maxcdn_form', w3tc_maxcdn_resize); + }) + + + + .on('click', '.w3tc_cdn_maxcdn_configure_zone_skip', function() { + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=cdn_maxcdn_configure_zone_skip'; + + W3tc_Lightbox.load_form(url, '.w3tc_cdn_maxcdn_form', w3tc_maxcdn_resize); + }) + + + + .on('click', '.w3tc_cdn_maxcdn_done', function() { + // refresh page + window.location = window.location + '&'; + }) + + + + .on('size_change', '#cdn_cname_add', function() { + w3tc_maxcdn_resize(W3tc_Lightbox); + }) +}); diff --git a/Cdn_MaxCdn_Page_View.php b/Cdn_MaxCdn_Page_View.php new file mode 100644 index 0000000..6f537ca --- /dev/null +++ b/Cdn_MaxCdn_Page_View.php @@ -0,0 +1,96 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + This website domain has to be CNAME pointing to this + CDN domain for HTTP requests + + + + + + + + + + +
+ + This website domain has to be CNAME pointing to this + CDN domain for HTTPS requests + + + + + + + + + +
CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' )?> + + + + + + get_array( 'cdn.maxcdn.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> +
CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' )?> + + + + + + + + + diff --git a/Cdn_MaxCdn_Popup.php b/Cdn_MaxCdn_Popup.php new file mode 100644 index 0000000..52f5cb8 --- /dev/null +++ b/Cdn_MaxCdn_Popup.php @@ -0,0 +1,282 @@ +render_intro( array( + 'api_key' => $config->get_string( 'cdn.maxcdn.authorization_key' ) ) ); + } + + + + private function render_intro( $details ) { + $config = Dispatcher::config(); + $url_obtain_key = Util_Ui::url( array( + 'page' => 'w3tc_dashboard', + 'w3tc_cdn_maxcdn_authorize' => 'y' + ) ); + + include W3TC_DIR . '/Cdn_MaxCdn_Popup_View_Intro.php'; + exit(); + } + + + + public function w3tc_ajax_cdn_maxcdn_list_zones() { + $api_key = $_REQUEST['api_key']; + + $api = \NetDNA::create( $api_key ); + if ( !$api->is_valid() ) { + $this->render_intro( array( + 'api_key' => $api_key, + 'error_message' => 'Can\'t authenticate: API key not valid' + ) ); + exit(); + } + + try { + $zones = $api->get_pull_zones(); + } catch ( \Exception $ex ) { + $error_message = 'Can\'t authenticate: ' . $ex->getMessage(); + + if ( strpos( $error_message, 'not whitelisted' ) > 0 ) { + $error_message .= '. You can whitelist IP ' . + 'here'; + } + $this->render_intro( array( + 'api_key' => $api_key, + 'error_message' => $error_message + ) ); + exit(); + } + + $details = array( + 'api_key' => $api_key, + 'zones' => $zones + ); + + include W3TC_DIR . '/Cdn_MaxCdn_Popup_View_Zones.php'; + exit(); + } + + + + public function w3tc_ajax_cdn_maxcdn_view_zone() { + $config = Dispatcher::config(); + $api_key = $_REQUEST['api_key']; + $zone_id = Util_Request::get( 'zone_id', '' ); + + $details = array( + 'api_key' => $api_key, + 'zone_id' => $zone_id, + 'name' => '', + 'url' => array( + 'new' => get_home_url() ), + 'compress' => array( + 'new' => 1 ), + 'ssl' => array( + // off, dedicated, sni, shared + 'current' => null, + 'new' => null + ), + 'cors_headers' => array( + 'new' => ( $config->get_boolean( 'cdn.cors_header') ? 0 : 1 ) ), + 'domains' => array() + ); + + if ( empty( $zone_id ) ) { + // create new zone mode + $details['name'] = Util_Request::get( 'zone_new_name' ); + } else { + $api = \NetDNA::create( $api_key ); + try { + $zone = $api->get_zone( $zone_id ); + $details['domains']['current'] = $api->get_custom_domains( $zone_id ); + } catch ( \Exception $ex ) { + $this->render_intro( array( + 'api_key' => $api_key, + 'error_message' => 'Can\'t obtain zone: ' . $ex->getMessage() + ) ); + exit(); + } + + $details['name'] = $zone['name']; + $details['compress']['current'] = $zone['compress']; + $details['cors_headers']['current'] = $zone['cors_headers']; + if ( $zone['ssl'] ) { + $details['ssl']['current'] = 'dedicated'; + } elseif ( $zone['ssl_sni'] ) { + $details['ssl']['current'] = 'sni'; + } elseif ( $zone['sslshared'] ) { + $details['ssl']['current'] = 'shared'; + } else { + $details['ssl']['current'] = 'off'; + } + $details['url']['current'] = $zone['url']; + } + + + if ( Util_Environment::is_https() && + ( is_null( $details['ssl']['current'] ) || + $details['ssl']['current'] == 'off' ) ) { + $details['ssl']['new'] = 'shared'; + } + + include W3TC_DIR . '/Cdn_MaxCdn_Popup_View_Zone.php'; + exit(); + } + + + + public function w3tc_ajax_cdn_maxcdn_configure_zone() { + $api_key = $_REQUEST['api_key']; + $zone_id = Util_Request::get( 'zone_id', '' ); + + if ( empty( $zone_id ) ) { + $zone = array( + 'name' => Util_Request::get( 'name' ), + 'label' => Util_Request::get( 'name' ), + 'url' => Util_Request::get( 'url' ), + 'use_stale' => 1, + 'queries' => 1, + 'compress' => 1, + 'backend_compress' => 1 + ); + } else { + $zone = array(); + + if ( isset( $_REQUEST['url_change'] ) ) { + $zone['url'] = Util_Request::get( 'url' ); + } + if ( isset( $_REQUEST['compress_change'] ) ) { + $zone['compress'] = Util_Request::get( 'compress' ); + } + if ( isset( $_REQUEST['cors_headers_change'] ) ) { + $zone['cors_headers'] = Util_Request::get( 'cors_headers' ); + } + if ( Util_Request::get( 'ssl' ) == 'shared' ) { + $zone['sslshared'] = 1; + $zone['http2'] = 1; + } + } + + $api = \NetDNA::create( $api_key ); + + try { + if ( empty( $zone_id ) ) { + $response = $api->create_pull_zone( $zone ); + $zone_id = $response['id']; + } else { + if ( count( array_keys( $zone ) ) > 0 ) { + $response = $api->update_pull_zone( $zone_id, $zone ); + } + } + + $response = $api->get_zone( $zone_id ); + } catch ( \Exception $ex ) { + $this->render_intro( array( + 'api_key' => $api_key, + 'error_message' => 'Failed to configure zone: ' . + $ex->getMessage() + ) ); + exit(); + } + + $c = Dispatcher::config(); + $domains = $c->get( 'cdn.maxcdn.domain' ); + $domains['http_default'] = $response['cdn_url']; + $domains['https_default'] = $response['ssl_url']; + + $c->set( 'cdn.maxcdn.authorization_key', $api_key ); + $c->set( 'cdn.maxcdn.zone_id', $zone_id ); + $c->set( 'cdn.maxcdn.domain', $domains ); + $c->save(); + + include W3TC_DIR . '/Cdn_MaxCdn_Popup_View_Success.php'; + exit(); + } + + + + private function render_zone_textbox_change( $details, $field ) { + Util_Ui::hidden( '', $field, $details[$field]['new'] ); + + if ( !isset( $details[$field]['current'] ) ) { + echo 'will be set to '; + echo htmlspecialchars( $details[$field]['new'] ); + echo ''; + } elseif ( $details[$field]['current'] == $details[$field]['new'] ) { + echo ''; + echo htmlspecialchars( $details[$field]['new'] ); + echo ''; + } else { + echo 'currently set to '; + echo htmlspecialchars( $details[$field]['current'] ); + echo '
'; + echo ' '; + echo 'change to '; + echo htmlspecialchars( $details[$field]['new'] ); + echo '
'; + } + } + + + private function render_zone_boolean_change( $details, $field ) { + Util_Ui::hidden( '', $field, $details[$field]['new'] ); + + if ( !isset( $details[$field]['current'] ) ) { + echo 'will be set to '; + $this->render_zone_boolean( $details[$field]['new'] ); + echo ''; + } elseif ( $details[$field]['current'] == $details[$field]['new'] ) { + echo ''; + $this->render_zone_boolean( $details[$field]['new'] ); + echo ''; + } else { + echo 'currently set to '; + $this->render_zone_boolean( $details[$field]['current'] ); + echo '
'; + echo ' '; + echo 'change to '; + $this->render_zone_boolean( $details[$field]['new'] ); + echo '
'; + } + } + + + + private function render_zone_boolean( $v ) { + if ( $v == 0 ) + echo 'disabled'; + else + echo 'enabled'; + } +} diff --git a/Cdn_MaxCdn_Popup_View_Intro.php b/Cdn_MaxCdn_Popup_View_Intro.php new file mode 100644 index 0000000..79d6380 --- /dev/null +++ b/Cdn_MaxCdn_Popup_View_Intro.php @@ -0,0 +1,39 @@ + +

+ ' . $details['error_message'] . ''; +?> +
+ + + + + + +
API Key: + +
+ + To obtain API key you can + click here, + log in, and paste the key in above field. + +
+ +

+ +

+ +
+
diff --git a/Cdn_MaxCdn_Popup_View_Success.php b/Cdn_MaxCdn_Popup_View_Success.php new file mode 100644 index 0000000..113e14c --- /dev/null +++ b/Cdn_MaxCdn_Popup_View_Success.php @@ -0,0 +1,23 @@ + +
+
+ + +
+ Pull Zone was successfully configured.
+
+ +

+ +

+ +
+
diff --git a/Cdn_MaxCdn_Popup_View_Zone.php b/Cdn_MaxCdn_Popup_View_Zone.php new file mode 100644 index 0000000..1f87294 --- /dev/null +++ b/Cdn_MaxCdn_Popup_View_Zone.php @@ -0,0 +1,74 @@ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
Name:
Origin URL:render_zone_textbox_change( $details, 'url' ) ?>
Compress content:render_zone_boolean_change( $details, 'compress' ) ?>
Add CORS header:render_zone_boolean_change( $details, 'cors_headers' ) ?>
HTTPS support: + + + +
+ + + + +
+ +
+ +
+ +

+ +

+ +
+
diff --git a/Cdn_MaxCdn_Popup_View_Zones.php b/Cdn_MaxCdn_Popup_View_Zones.php new file mode 100644 index 0000000..d616a16 --- /dev/null +++ b/Cdn_MaxCdn_Popup_View_Zones.php @@ -0,0 +1,53 @@ + +
+ +
+ + + + + + +
Zone: + 15 ) + echo '
'; +?> + + +
+ + + + + + 15 ) + echo '
'; +?> +
+ +

+ +

+ +
+
diff --git a/Cdn_Page.php b/Cdn_Page.php index d25fff5..39290a1 100644 --- a/Cdn_Page.php +++ b/Cdn_Page.php @@ -20,11 +20,6 @@ function view() { $config = Dispatcher::config(); $cdn_engine = $config->get_string( 'cdn.engine' ); - if ( Cdn_Util::is_engine_fsd( $cdn_engine ) ) { - do_action( 'w3tc_settings_cdn' ); - return; - } - $cdn_enabled = $config->get_boolean( 'cdn.enabled' ); $cdn_mirror = Cdn_Util::is_engine_mirror( $cdn_engine ); $cdn_mirror_purge_all = Cdn_Util::can_purge_all( $cdn_engine ); @@ -45,33 +40,6 @@ function view() { // Required for Update Media Query String button $browsercache_enabled = $config->get_boolean( 'browsercache.enabled' ); $browsercache_update_media_qs = ( $config->get_boolean( 'browsercache.cssjs.replace' ) || $config->get_boolean( 'browsercache.other.replace' ) ); - if ( in_array( $cdn_engine, array( 'netdna', 'maxcdn' ) ) ) { - $pull_zones = array(); - $authorization_key = $config->get_string( "cdn.$cdn_engine.authorization_key" ); - $zone_id = $config->get_integer( "cdn.$cdn_engine.zone_id" ); - $alias = $consumerkey = $consumersecret = ''; - - if ( $authorization_key ) { - $keys = explode( '+', $authorization_key ); - if ( sizeof( $keys ) == 3 ) { - list( $alias, $consumerkey, $consumersecret ) = $keys; - } - } - - $authorized = $authorization_key != '' && $alias && $consumerkey && $consumersecret; - $have_zone = $zone_id != 0; - if ( $authorized ) { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - try { - $api = new \NetDNA( $alias, $consumerkey, $consumersecret ); - $pull_zones = $api->get_zones_by_url( get_home_url() ); - } catch ( \Exception $ex ) { - - - Util_Ui::error_box( '

There is an error with your CDN settings: ' . $ex->getMessage() . '

' ); - } - } - } include W3TC_INC_DIR . '/options/cdn.php'; } diff --git a/Cdn_Page_View_Fsd_HeaderActions.php b/Cdn_Page_View_Fsd_HeaderActions.php index 3d98050..5c3e183 100644 --- a/Cdn_Page_View_Fsd_HeaderActions.php +++ b/Cdn_Page_View_Fsd_HeaderActions.php @@ -8,7 +8,7 @@

CDN completely', 'w3-total-cache' ), Util_Ui::url( array( 'w3tc_cdn_flush' => 'y' ) ) ); ?>

diff --git a/Cdn_Plugin.php b/Cdn_Plugin.php index eb1034e..36f95f7 100644 --- a/Cdn_Plugin.php +++ b/Cdn_Plugin.php @@ -29,21 +29,16 @@ function __construct() { */ function run() { $cdn_engine = $this->_config->get_string( 'cdn.engine' ); - if ( Cdn_Util::is_engine_fsd( $cdn_engine ) ) { - $this->run_fsd(); - return; - } add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) ); - if ( !$this->_config->get_boolean( 'cdn.debug' ) ) - add_filter( 'w3tc_footer_comment', array( - $this, - 'w3tc_footer_comment' - ) ); + add_filter( 'w3tc_footer_comment', array( + $this, + 'w3tc_footer_comment' + ) ); if ( !Cdn_Util::is_engine_mirror( $cdn_engine ) ) { add_action( 'delete_attachment', array( @@ -80,6 +75,11 @@ function run() { $this, 'update_feedback' ) ); + + add_filter( 'wp_prepare_attachment_for_js', array( + $this, + 'wp_prepare_attachment_for_js' + ), 0 ); } add_filter( 'w3tc_admin_bar_menu', @@ -111,24 +111,24 @@ function run() { */ private function run_fsd() { add_action( 'w3tc_flush_all', array( - '\W3TC\Cdn_Fsd_CacheFlush', + '\W3TC\Cdnfsd_CacheFlush', 'w3tc_flush_all' ), 3000, 1 ); add_action( 'w3tc_flush_post', array( - '\W3TC\Cdn_Fsd_CacheFlush', + '\W3TC\Cdnfsd_CacheFlush', 'w3tc_flush_post' ), 3000, 1 ); add_action( 'w3tc_flushable_posts', '__return_true', 3000 ); add_action( 'w3tc_flush_posts', array( - '\W3TC\Cdn_Fsd_CacheFlush', + '\W3TC\Cdnfsd_CacheFlush', 'w3tc_flush_all' ), 3000 ); add_action( 'w3tc_flush_url', array( - '\W3TC\Cdn_Fsd_CacheFlush', + '\W3TC\Cdnfsd_CacheFlush', 'w3tc_flush_url' ), 3000, 1 ); add_filter( 'w3tc_flush_execute_delayed_operations', array( - '\W3TC\Cdn_Fsd_CacheFlush', + '\W3TC\Cdnfsd_CacheFlush', 'w3tc_flush_execute_delayed_operations' ), 3000 ); @@ -685,6 +685,54 @@ function change_canonical_header() { $admin->change_canonical_header(); } + /** + * Adjusts attachment urls to cdn. This is for those who rely on + * wp_prepare_attachment_for_js() + * + * @param array $response Mixed collection of data about the attachment object + * @return array + */ + public function wp_prepare_attachment_for_js( $response ) { + $response['url'] = $this->wp_prepare_attachment_for_js_url( $response['url'] ); + $response['link'] = $this->wp_prepare_attachment_for_js_url( $response['link'] ); + + if ( !empty( $response['sizes'] ) ) { + foreach( $response['sizes'] as $size => &$data ) { + $data['url'] = $this->wp_prepare_attachment_for_js_url( $data['url'] ); + } + } + + return $response; + } + + /** + * An attachment's local url to modify into a cdn url + * + * @param string $url the local url to modify + * @return string + */ + private function wp_prepare_attachment_for_js_url( $url ) { + $url = trim( $url ); + if ( !empty( $url ) ) { + $parsed = parse_url( $url ); + $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) . + ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' ); + + $wp_upload_dir = wp_upload_dir(); + $upload_base_url = $wp_upload_dir['baseurl']; + + if ( substr($url, 0, strlen( $upload_base_url ) ) == $upload_base_url ) { + $common = Dispatcher::component( 'Cdn_Core' ); + $new_url = $common->url_to_cdn_url( $url, $uri ); + if ( !is_null( $new_url ) ) { + $url = $new_url; + } + } + } + + return $url; + } + public function w3tc_admin_bar_menu( $menu_items ) { $cdn_engine = $this->_config->get_string( 'cdn.engine' ); @@ -724,14 +772,8 @@ public function w3tc_footer_comment( $strings ) { sprintf( ' (%s)', $this->cdn_reject_reason ) ) ); if ( $this->_config->get_boolean( 'cdn.debug' ) ) { - $strings[] = "CDN debug info:"; - $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), - $this->_config->get_string( 'cdn.engine' ) ); - - if ( $this->cdn_reject_reason ) { - $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), - $this->cdn_reject_reason ); - } + $strings[] = ''; + $strings[] = 'CDN debug info:'; if ( count( $this->_replaced_urls ) ) { $strings[] = "Replaced URLs:"; @@ -742,6 +784,7 @@ public function w3tc_footer_comment( $strings ) { Util_Content::escape_comment( $new_url ) ); } } + $strings[] = ''; } return $strings; @@ -1130,15 +1173,8 @@ function _link_replace_callback_checks( $match, $quote, $url, $path ) { */ function _link_replace_callback_ask_cdn( $match, $quote, $url, $path ) { $common = Dispatcher::component( 'Cdn_Core' ); - $cdn = $common->get_cdn(); - $remote_path = $common->uri_to_cdn_uri( $path ); - $new_url = $cdn->format_url( $remote_path ); - if ( $new_url ) { - $is_engine_mirror = Cdn_Util::is_engine_mirror( - $this->_config->get_string( 'cdn.engine' ) ); - - $new_url = apply_filters( 'w3tc_cdn_url', $new_url, $url, - $is_engine_mirror ); + $new_url = $common->url_to_cdn_url( $url, $path ); + if ( !is_null( $new_url ) ) { $this->replaced_urls[$url] = $new_url; return $quote . $new_url; } diff --git a/Cdn_Plugin_Admin.php b/Cdn_Plugin_Admin.php index 3187a39..944c2fb 100644 --- a/Cdn_Plugin_Admin.php +++ b/Cdn_Plugin_Admin.php @@ -9,26 +9,25 @@ function run() { $c = Dispatcher::config(); $cdn_engine = $c->get_string( 'cdn.engine' ); - if ( $c->get_boolean( 'cdn.enabled' ) && - !Cdn_Util::is_engine_fsd( $cdn_engine ) ) { + if ( $c->get_boolean( 'cdn.enabled' ) ) { $admin_notes = new Cdn_AdminNotes(); add_filter( 'w3tc_notes', array( $admin_notes, 'w3tc_notes' ) ); add_filter( 'w3tc_errors', array( $admin_notes, 'w3tc_errors' ) ); + + if ( $c->get_boolean( 'cdn.admin.media_library' ) && + $c->get_boolean( 'cdn.uploads.enable' ) ) { + + add_filter( 'wp_get_attachment_url', + array( $this, 'wp_get_attachment_url' ), 0 ); + + add_filter( 'attachment_link', + array( $this, 'wp_get_attachment_url' ), 0 ); + } } // attach to actions without firing class loading at all without need - if ( $cdn_engine == 'cloudfront_fsd' ) { - add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( - '\W3TC\Cdn_CloudFrontFsd_Page', - 'admin_print_scripts_w3tc_cdn' ) ); - add_action( 'w3tc_ajax', array( - '\W3TC\Cdn_CloudFrontFsd_Popup', - 'w3tc_ajax' ) ); - add_action( 'w3tc_settings_cdn', array( - '\W3TC\Cdn_CloudFrontFsd_Page', - 'w3tc_settings_cdn' ) ); - } elseif ( $cdn_engine == 'google_drive' ) { + if ( $cdn_engine == 'google_drive' ) { add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( '\W3TC\Cdn_GoogleDrive_Page', 'admin_print_scripts_w3tc_cdn' ) ); @@ -52,16 +51,17 @@ function run() { add_action( 'w3tc_settings_cdn_boxarea_configuration', array( '\W3TC\Cdn_Highwinds_Page', 'w3tc_settings_cdn_boxarea_configuration' ) ); - } elseif ( $cdn_engine == 'maxcdn_fsd' ) { + } elseif ( $cdn_engine == 'maxcdn' ) { add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( - '\W3TC\Cdn_MaxCdnFsd_Page', + '\W3TC\Cdn_MaxCdn_Page', 'admin_print_scripts_w3tc_cdn' ) ); add_action( 'w3tc_ajax', array( - '\W3TC\Cdn_MaxCdnFsd_Popup', + '\W3TC\Cdn_MaxCdn_Popup', 'w3tc_ajax' ) ); - add_action( 'w3tc_settings_cdn', array( - '\W3TC\Cdn_MaxCdnFsd_Page', - 'w3tc_settings_cdn' ) ); + add_action( 'w3tc_settings_cdn_boxarea_configuration', array( + '\W3TC\Cdn_MaxCdn_Page', + 'w3tc_settings_cdn_boxarea_configuration' + ) ); } elseif ( $cdn_engine == 'rackspace_cdn' ) { add_filter( 'w3tc_admin_actions', array( '\W3TC\Cdn_RackSpaceCdn_Page', @@ -101,25 +101,8 @@ public function w3tc_settings_general_boxarea_cdn() { $engine_optgroups = array(); $engine_values = array(); - $is_fsd = Util_Environment::is_w3tc_pro( $config ); - - if ( $is_fsd ) { - $engine_optgroups[] = __( 'Full Site Delivery:', 'w3-total-cache' ); - $engine_values['cloudfront_fsd'] = array( - 'label' => __( 'Amazon CloudFront', 'w3-total-cache' ), - 'optgroup' => 0 - ); - $engine_values['maxcdn_fsd'] = array( - 'label' => __( 'MaxCDN (recommended)', 'w3-total-cache' ), - 'optgroup' => 0 - ); - - $optgroup_pull = count( $engine_optgroups ); - $engine_optgroups[] = __( 'Origin Pull / Mirror:', 'w3-total-cache' ); - } else { - $optgroup_pull = count( $engine_optgroups ); - $engine_optgroups[] = __( 'Origin Pull / Mirror:', 'w3-total-cache' ); - } + $optgroup_pull = count( $engine_optgroups ); + $engine_optgroups[] = __( 'Origin Pull / Mirror:', 'w3-total-cache' ); $optgroup_push = count( $engine_optgroups ); $engine_optgroups[] = __( 'Origin Push:', 'w3-total-cache' ); @@ -154,10 +137,6 @@ public function w3tc_settings_general_boxarea_cdn() { 'label' => __( 'MaxCDN', 'w3-total-cache' ), 'optgroup' => $optgroup_pull ); - $engine_values['netdna'] = array( - 'label' => __( 'MaxCDN Enterprise (NetDNA)', 'w3-total-cache' ), - 'optgroup' => $optgroup_pull - ); $engine_values['rackspace_cdn'] = array( 'label' => __( 'RackSpace CDN', 'w3-total-cache' ), 'optgroup' => $optgroup_pull @@ -201,22 +180,42 @@ public function w3tc_settings_general_boxarea_cdn() { ); $cdn_enabled = $config->get_boolean( 'cdn.enabled' ); - $cdn_engine = $config->get_string( 'cdn.engine' ); - $tag = ''; - if ( $cdn_engine == 'cloudfront_fsd' ) - $tag = '#cdn-fsd-cloudfront'; - elseif ( $cdn_engine == 'maxcdn_fsd' ) - $tag = '#cdn-fsd-maxcdn'; - - if ( empty( $tag ) ) - $cdn_engine_extra_description = ''; - else - $cdn_engine_extra_description = - ' See setup instructions'; - include W3TC_DIR . '/Cdn_GeneralPage_View.php'; } + + + + /** + * Adjusts attachment urls to cdn. This is for those who rely on + * wp_get_attachment_url() + * + * @param string $url the local url to modify + * @return string + */ + function wp_get_attachment_url( $url ) { + if ( defined( 'WP_ADMIN' ) ) { + $url = trim( $url ); + + if ( !empty( $url ) ) { + $parsed = parse_url( $url ); + $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) . + ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' ); + + $wp_upload_dir = wp_upload_dir(); + $upload_base_url = $wp_upload_dir['baseurl']; + + if ( substr($url, 0, strlen( $upload_base_url ) ) == $upload_base_url ) { + $common = Dispatcher::component( 'Cdn_Core' ); + $new_url = $common->url_to_cdn_url( $url, $uri ); + if ( !is_null( $new_url ) ) { + $url = $new_url; + } + } + } + } + + return $url; + } } diff --git a/Cdn_Plugin_WidgetMaxCdn.php b/Cdn_Plugin_WidgetMaxCdn.php index 2746a6f..96ffb02 100644 --- a/Cdn_Plugin_WidgetMaxCdn.php +++ b/Cdn_Plugin_WidgetMaxCdn.php @@ -9,16 +9,7 @@ class Cdn_Plugin_WidgetMaxCdn { private $have_zone; private $_sealed; - /** - * - * - * @var NetDNA - */ private $api; - - /** - * Config - */ private $_config = null; function __construct() { diff --git a/Cdn_Plugin_WidgetNetDna.php b/Cdn_Plugin_WidgetNetDna.php deleted file mode 100644 index 2e9082c..0000000 --- a/Cdn_Plugin_WidgetNetDna.php +++ /dev/null @@ -1,191 +0,0 @@ -_config = Dispatcher::config(); - } - - /** - * Runs plugin - */ - function run() { - if ( Util_Admin::get_current_wp_page() == 'w3tc_dashboard' ) - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); - add_action( 'w3tc_widget_setup', array( - $this, - 'wp_dashboard_setup' - ) ); - add_action( 'w3tc_network_dashboard_setup', array( - $this, - 'wp_dashboard_setup' - ) ); - - // Configure authorize and have_zone - $this->_setup( $this->_config ); - - if ( $this->have_zone && $this->authorized && isset( $_GET['page'] ) && strpos( $_GET['page'], 'w3tc_dashboard' ) !== false ) { - require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; - require_once W3TC_LIB_NETDNA_DIR . '/NetDNAPresentation.php'; - $authorization_key = $this->_config->get_string( 'cdn.netdna.authorization_key' ); - $alias = $consumerkey = $consumersecret = ''; - - $keys = explode( '+', $authorization_key ); - if ( sizeof( $keys ) == 3 ) - list( $alias, $consumerkey, $consumersecret ) = $keys; - - $this->api = new \NetDNA( $alias, $consumerkey, $consumersecret ); - add_action( 'admin_head', array( $this, 'admin_head' ) ); - } - } - - function admin_head() { - $zone_id = $this->_config->get_string( 'cdn.netdna.zone_id' ); - try { - $zone_info = $this->api->get_pull_zone( $zone_id ); - - if ( !$zone_info ) - return; - $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id ); - - if ( !isset( $filetypes['filetypes'] ) ) - return; - } catch ( \Exception $ex ) { - return; - } - - $filetypes = $filetypes['filetypes']; - $group_hits = \NetDNAPresentation::group_hits_per_filetype_group( $filetypes ); - - $list = array(); - $colors = array(); - foreach ( $group_hits as $group => $hits ) { - $list[] = sprintf( "['%s', %d]", $group, $hits ); - $colors[] = '\'' . \NetDNAPresentation::get_file_group_color( $group ) . '\''; - } -?> - - -', - array( $this, 'widget_netdna' ), - Util_Ui::admin_url( 'admin.php?page=w3tc_cdn' ), - 'normal' ); - } - - /** - * Loads and configures NetDNA widget to be used in WP Dashboards. - * - * @param unknown $widget_id - * @param array $form_inputs - */ - function widget_netdna( $widget_id, $form_inputs = array() ) { - $authorized = $this->authorized; - $have_zone = $this->have_zone; - $is_sealed = $this->_sealed; - $error = ''; - $pull_zones = array(); - $zone_info = false; - if ( $authorized && $have_zone ) { - $zone_id = $this->_config->get_integer( 'cdn.netdna.zone_id' ); - try{ - $zone_info = $this->api->get_pull_zone( $zone_id ); - } catch ( \Exception $ex ) { - $error = $ex->getMessage(); - $zone_info = false; - } - if ( $zone_info ) { - $content_zone = $zone_info['name']; - try{ - $summary = $this->api->get_stats_per_zone( $zone_id ); - $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id ); - $popular_files = $this->api->get_list_of_popularfiles_per_zone( $zone_id ); - $popular_files = \NetDNAPresentation::format_popular( $popular_files ); - $popular_files = array_slice( $popular_files, 0 , 5 ); - $account = $this->api->get_account(); - $account_status = \NetDNAPresentation::get_account_status( $account['status'] ); - include W3TC_INC_WIDGET_DIR . '/netdna.php'; - } catch ( \Exception $ex ) { - try { - $pull_zones = $this->api->get_zones_by_url( home_url() ); - } catch ( \Exception $ex ) {} - $error = $ex->getMessage(); - include W3TC_INC_WIDGET_DIR . '/netdna_signup.php'; - } - } else { - try { - $pull_zones = $this->api->get_zones_by_url( home_url() ); - } catch ( \Exception $ex ) {} - include W3TC_INC_WIDGET_DIR . '/netdna_signup.php'; - } - } else { - include W3TC_INC_WIDGET_DIR . '/netdna_signup.php'; - } - } - - /** - * - * - * @param Config $config - */ - private function _setup( $config ) { - $this->authorized = $config->get_string( 'cdn.netdna.authorization_key' ) != '' && - $config->get_string( 'cdn.engine' ) == 'netdna'; - $keys = explode( '+', $config->get_string( 'cdn.netdna.authorization_key' ) ); - $this->authorized = $this->authorized && sizeof( $keys ) == 3; - - $this->have_zone = $config->get_string( 'cdn.netdna.zone_id' ) != 0; - } - - public function enqueue() { - wp_enqueue_style( 'w3tc-widget' ); - wp_enqueue_script( 'w3tc-metadata' ); - wp_enqueue_script( 'w3tc-widget' ); - } -} diff --git a/Cdn_RackSpaceCdn_Page_View.js b/Cdn_RackSpaceCdn_Page_View.js index cdc8b75..0aa715e 100644 --- a/Cdn_RackSpaceCdn_Page_View.js +++ b/Cdn_RackSpaceCdn_Page_View.js @@ -97,7 +97,7 @@ jQuery(function($) { var protocol = ''; $('body').find('.w3tc_cdn_rackspace_protocol').each(function(i) { - if (!jQuery(this).attr('checked')) + if (!jQuery(this).prop('checked')) return; protocol = $(this).val(); diff --git a/Cdn_RackSpaceCdn_Page_View.php b/Cdn_RackSpaceCdn_Page_View.php index f1a287b..d66e80d 100644 --- a/Cdn_RackSpaceCdn_Page_View.php +++ b/Cdn_RackSpaceCdn_Page_View.php @@ -38,7 +38,7 @@ - + @@ -52,7 +52,7 @@ value="" />
- CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> + CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> @@ -66,7 +66,7 @@ class="w3tc-button-save button" type="submit" value="" />
- CDN host, this value will replace your site\'s hostname in the HTML. You can manage them from RackSpace management console and load here afterwards.', 'w3-total-cache' ); ?> + CDN host, this value will replace your site\'s hostname in the HTML. You can manage them from RackSpace management console and load here afterwards.', 'w3-total-cache' ); ?> diff --git a/Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php b/Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php index 8ad24f5..198ca19 100644 --- a/Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php +++ b/Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php @@ -13,7 +13,7 @@
- CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> + CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>

- + http:
https: @@ -52,7 +52,7 @@ -
CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> @@ -61,7 +61,7 @@ get_array( 'cdn.rscf.cname' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
- CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> + CDN host, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> diff --git a/Cdn_Util.php b/Cdn_Util.php index 8a6dee5..8c655c2 100644 --- a/Cdn_Util.php +++ b/Cdn_Util.php @@ -14,17 +14,14 @@ static public function is_engine( $engine ) { 'att', 'azure', 'cf', - 'cloudfront_fsd', 'cf2', 'cotendo', 'edgecast', - 'maxcdn_fsd', 'ftp', 'google_drive', 'highwinds', 'maxcdn', 'mirror', - 'netdna', 'rscf', 'rackspace_cdn', 's3', @@ -40,25 +37,12 @@ static public function is_engine( $engine ) { */ static public function is_engine_mirror( $engine ) { return in_array( $engine, array( - 'mirror', 'netdna', 'maxcdn', 'cotendo', 'cf2', 'akamai', + 'mirror', 'maxcdn', 'cotendo', 'cf2', 'akamai', 'edgecast', 'att', 'highwinds', 'rackspace_cdn' ) ); } - /** - * Returns true if CDN engine is mirror - * - * @param string $engine - * @return bool - */ - static public function is_engine_fsd( $engine ) { - return in_array( $engine, array( - 'cloudfront_fsd', - 'maxcdn_fsd' - ) ); - } - static public function is_engine_push( $engine ) { - return !self::is_engine_mirror( $engine ) && !self::is_engine_fsd( $engine ); + return !self::is_engine_mirror( $engine ); } /** @@ -72,10 +56,8 @@ static public function can_purge_all( $engine ) { 'att', 'cotendo', 'edgecast', - 'maxcdn_fsd', 'highwinds', 'maxcdn', - 'netdna', ) ); } @@ -92,14 +74,11 @@ static public function can_purge( $engine ) { 'azure', 'cf', 'cf2', - 'cloudfront_fsd', 'cotendo', 'edgecast', - 'maxcdn_fsd', 'ftp', 'highwinds', 'maxcdn', - 'netdna', 'rscf', 's3', 's3_compatible', diff --git a/Cdn_Fsd_CacheFlush.php b/Cdnfsd_CacheFlush.php similarity index 91% rename from Cdn_Fsd_CacheFlush.php rename to Cdnfsd_CacheFlush.php index 6e61b14..13f35e7 100644 --- a/Cdn_Fsd_CacheFlush.php +++ b/Cdnfsd_CacheFlush.php @@ -4,7 +4,7 @@ /** * CDN cache flusher */ -class Cdn_Fsd_CacheFlush { +class Cdnfsd_CacheFlush { /** * Array of urls to flush * @@ -22,7 +22,7 @@ static public function w3tc_flush_all( $extras = array() ) { if ( isset( $extras['only'] ) && $extras['only'] != 'cdn' ) return; - $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' ); + $o = Dispatcher::component( 'Cdnfsd_CacheFlush' ); $o->flush_all_requested = true; return true; @@ -167,7 +167,7 @@ static public function w3tc_flush_post( $post_id ) { * Queue flush */ if ( count( $full_urls ) ) { - $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' ); + $o = Dispatcher::component( 'Cdnfsd_CacheFlush' ); foreach ( $full_urls as $url ) $o->queued_urls[$url] = '*'; @@ -182,7 +182,7 @@ static public function w3tc_flush_post( $post_id ) { * @param unknown $url */ static public function w3tc_flush_url( $url ) { - $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' ); + $o = Dispatcher::component( 'Cdnfsd_CacheFlush' ); $o->queued_urls[$url] = '*'; return true; @@ -192,14 +192,18 @@ static public function w3tc_flush_url( $url ) { * Clears global and repeated urls */ static public function w3tc_flush_execute_delayed_operations( $actions_made ) { - $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' ); + $o = Dispatcher::component( 'Cdnfsd_CacheFlush' ); if ( $o->flush_all_requested ) { - $core = Dispatcher::component( 'Cdn_Fsd_Core' ); + $core = Dispatcher::component( 'Cdnfsd_Core' ); $engine = $core->get_engine(); + + try { - $engine->flush_all(); - $actions_made[] = array( 'module' => 'cdn' ); + if ( !is_null( $engine ) ) { + $engine->flush_all(); + $actions_made[] = array( 'module' => 'cdn' ); + } } catch ( \Exception $ex ) { $actions_made[] = array( 'module' => 'cdn', @@ -214,11 +218,13 @@ static public function w3tc_flush_execute_delayed_operations( $actions_made ) { if ( $count > 0 ) { $urls = array_keys( $o->queued_urls ); - $core = Dispatcher::component( 'Cdn_Fsd_Core' ); + $core = Dispatcher::component( 'Cdnfsd_Core' ); $engine = $core->get_engine(); try { - $engine->flush_urls( $urls ); - $actions_made[] = array( 'module' => 'cdn' ); + if ( !is_null( $engine ) ) { + $engine->flush_urls( $urls ); + $actions_made[] = array( 'module' => 'cdn' ); + } } catch ( \Exception $ex ) { $actions_made[] = array( 'module' => 'cdn', diff --git a/Cdn_CloudFrontFsd_Api.php b/Cdnfsd_CloudFront_Api.php similarity index 99% rename from Cdn_CloudFrontFsd_Api.php rename to Cdnfsd_CloudFront_Api.php index 193d640..d46dd6d 100644 --- a/Cdn_CloudFrontFsd_Api.php +++ b/Cdnfsd_CloudFront_Api.php @@ -1,7 +1,7 @@ distribution_id ) ) throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) ); - $api = new Cdn_CloudFrontFsd_Api( $this->access_key, $this->secret_key ); + $api = new Cdnfsd_CloudFront_Api( $this->access_key, $this->secret_key ); $uris = array(); foreach ( $urls as $url ) { $parsed = parse_url( $url ); @@ -46,7 +46,7 @@ function flush_all() { empty( $this->distribution_id ) ) throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) ); - $api = new Cdn_CloudFrontFsd_Api( $this->access_key, $this->secret_key ); + $api = new Cdnfsd_CloudFront_Api( $this->access_key, $this->secret_key ); $uris = array( '/*' ); $api->invalidation_create( $this->distribution_id, $uris ); diff --git a/Cdnfsd_CloudFront_Page.php b/Cdnfsd_CloudFront_Page.php new file mode 100644 index 0000000..6c5ff54 --- /dev/null +++ b/Cdnfsd_CloudFront_Page.php @@ -0,0 +1,18 @@ +get_string( 'cdn.cloudfront_fsd.access_key' ); +$key = $config->get_string( 'cdnfsd.cloudfront.access_key' ); $authorized = !empty( $key ); -include W3TC_DIR . '/Cdn_Page_View_Header.php'; ?> -

- - -

- @@ -45,15 +40,15 @@ @@ -64,6 +59,3 @@ - - 'y' ) ); - include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Intro.php'; + include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Intro.php'; exit(); } @@ -44,7 +44,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_list_distributions() { $access_key = $_REQUEST['access_key']; $secret_key = $_REQUEST['secret_key']; - $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key ); + $api = new Cdnfsd_CloudFront_Api( $access_key, $secret_key ); if ( empty( $access_key ) || empty( $secret_key ) ) { $this->render_intro( array( 'error_message' => 'Can\'t authenticate: Access Key or Secret not valid' @@ -82,7 +82,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_list_distributions() { 'distributions' => $items ); - include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Distributions.php'; + include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Distributions.php'; exit(); } @@ -119,7 +119,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_view_distribution() { // create new zone mode $details['distribution_comment'] = Util_Request::get( 'comment_new' ); } else { - $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key ); + $api = new Cdnfsd_CloudFront_Api( $access_key, $secret_key ); try { $distribution = $api->distribution_get( $distribution_id ); @@ -171,7 +171,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_view_distribution() { - include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Distribution.php'; + include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Distribution.php'; exit(); } @@ -305,7 +305,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution() { ); try { - $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key ); + $api = new Cdnfsd_CloudFront_Api( $access_key, $secret_key ); if ( empty( $distribution_id ) ) { $distribution['DefaultCacheBehavior']['TrustedSigners'] = array( 'Enabled' => 'false', @@ -329,10 +329,10 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution() { $distribution_domain = $response['DomainName']; $c = Dispatcher::config(); - $c->set( 'cdn.cloudfront_fsd.access_key', $access_key ); - $c->set( 'cdn.cloudfront_fsd.secret_key', $secret_key ); - $c->set( 'cdn.cloudfront_fsd.distribution_id', $distribution_id ); - $c->set( 'cdn.cloudfront_fsd.distribution_domain', $distribution_domain ); + $c->set( 'cdnfsd.cloudfront.access_key', $access_key ); + $c->set( 'cdnfsd.cloudfront.secret_key', $secret_key ); + $c->set( 'cdnfsd.cloudfront.distribution_id', $distribution_id ); + $c->set( 'cdnfsd.cloudfront.distribution_domain', $distribution_domain ); $c->save(); $details = array( @@ -341,7 +341,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution() { 'dns_cname_target' => $distribution_domain, ); - include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Success.php'; + include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Success.php'; exit(); } @@ -355,7 +355,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution_skip() { $origin_id = rand(); try { - $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key ); + $api = new Cdnfsd_CloudFront_Api( $access_key, $secret_key ); $distribution = $api->distribution_get( $distribution_id ); } catch ( \Exception $ex ) { $this->render_intro( array( @@ -370,10 +370,10 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution_skip() { $distribution_domain = 'n/a'; $c = Dispatcher::config(); - $c->set( 'cdn.cloudfront_fsd.access_key', $access_key ); - $c->set( 'cdn.cloudfront_fsd.secret_key', $secret_key ); - $c->set( 'cdn.cloudfront_fsd.distribution_id', $distribution_id ); - $c->set( 'cdn.cloudfront_fsd.distribution_domain', $distribution_domain ); + $c->set( 'cdnfsd.cloudfront.access_key', $access_key ); + $c->set( 'cdnfsd.cloudfront.secret_key', $secret_key ); + $c->set( 'cdnfsd.cloudfront.distribution_id', $distribution_id ); + $c->set( 'cdnfsd.cloudfront.distribution_domain', $distribution_domain ); $c->save(); $details = array( @@ -382,7 +382,7 @@ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution_skip() { 'dns_cname_target' => $distribution_domain, ); - include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Success.php'; + include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Success.php'; exit(); } } diff --git a/Cdn_CloudFrontFsd_Popup_View_Distribution.php b/Cdnfsd_CloudFront_Popup_View_Distribution.php similarity index 100% rename from Cdn_CloudFrontFsd_Popup_View_Distribution.php rename to Cdnfsd_CloudFront_Popup_View_Distribution.php diff --git a/Cdn_CloudFrontFsd_Popup_View_Distributions.php b/Cdnfsd_CloudFront_Popup_View_Distributions.php similarity index 100% rename from Cdn_CloudFrontFsd_Popup_View_Distributions.php rename to Cdnfsd_CloudFront_Popup_View_Distributions.php diff --git a/Cdn_CloudFrontFsd_Popup_View_Intro.php b/Cdnfsd_CloudFront_Popup_View_Intro.php similarity index 94% rename from Cdn_CloudFrontFsd_Popup_View_Intro.php rename to Cdnfsd_CloudFront_Popup_View_Intro.php index 0d2bb2b..80d7de0 100644 --- a/Cdn_CloudFrontFsd_Popup_View_Intro.php +++ b/Cdnfsd_CloudFront_Popup_View_Intro.php @@ -18,7 +18,7 @@ @@ -26,7 +26,7 @@
- + get_string( 'cdn.cloudfront_fsd.distribution_domain' ) +echo $config->get_string( 'cdnfsd.cloudfront.distribution_domain' ) ?>
This website domain has to be CNAME pointing to this - CDN domain + CDN domain
+ value="get_string( 'cdnfsd.cloudfront.access_key' ) ?>" />
+ value="get_string( 'cdnfsd.cloudfront.secret_key' ) ?>" />
diff --git a/Cdn_CloudFrontFsd_Popup_View_Success.php b/Cdnfsd_CloudFront_Popup_View_Success.php similarity index 100% rename from Cdn_CloudFrontFsd_Popup_View_Success.php rename to Cdnfsd_CloudFront_Popup_View_Success.php diff --git a/Cdnfsd_Core.php b/Cdnfsd_Core.php new file mode 100644 index 0000000..d5effb5 --- /dev/null +++ b/Cdnfsd_Core.php @@ -0,0 +1,54 @@ +get_string( 'cdnfsd.engine' ); + + switch ( $engine ) { + case 'cloudflare': + $engine_object = null; // extension handles everything + break; + + case 'cloudfront': + $engine_object = new Cdnfsd_CloudFront_Engine( array( + 'access_key' => $c->get_string( 'cdnfsd.cloudfront.access_key' ), + 'secret_key' => $c->get_string( 'cdnfsd.cloudfront.secret_key' ), + 'distribution_id' => $c->get_string( 'cdnfsd.cloudfront.distribution_id' ) + ) ); + break; + + case 'limelight': + $engine_object = new Cdnfsd_Limelight_Engine( array( + 'short_name' => $c->get_string( 'cdnfsd.limelight.short_name' ), + 'username' => $c->get_string( 'cdnfsd.limelight.username' ), + 'api_key' => $c->get_string( 'cdnfsd.limelight.api_key' ), + 'debug' => $c->get_string( 'cdnfsd.debug' ) + ) ); + break; + + case 'maxcdn': + $engine_object = new Cdnfsd_MaxCdn_Engine( array( + 'api_key' => $c->get_string( 'cdnfsd.maxcdn.api_key' ), + 'zone_id' => $c->get_integer( 'cdnfsd.maxcdn.zone_id' ) + ) ); + break; + + default: + throw new \Exception( 'unknown engine ' . $engine ); + } + } + + return $engine_object; + } +} diff --git a/Cdnfsd_GeneralPage_View.php b/Cdnfsd_GeneralPage_View.php new file mode 100644 index 0000000..a6b72a8 --- /dev/null +++ b/Cdnfsd_GeneralPage_View.php @@ -0,0 +1,37 @@ + +

+ +CDN provider try MaxCDN. Sign up and save 25%.', 'w3-total-cache' ), wp_nonce_url( Util_Ui::admin_url( 'admin.php?page=w3tc_dashboard&w3tc_cdn_maxcdn_signup' ), 'w3tc' ) ); ?> + +

+ + 'cdnfsd.enabled', + 'label' => __( 'FSDCDN:', 'w3-total-cache' ), + 'control' => 'checkbox', + 'checkbox_label' => __( 'Enable', 'w3-total-cache' ), + 'disabled' => ( $is_pro ? null : true ), + 'description' => __( 'Whole website will appear to load instantly for site visitors.', + 'w3-total-cache' ) . + ( $is_pro ? '' : __( ' Available after upgrade.', 'w3-total-cache' ) ) + ) ); + +Util_Ui::config_item( array( + 'key' => 'cdnfsd.engine', + 'label' => __( 'FSDCDN Type:', 'w3-total-cache' ), + 'control' => 'selectbox', + 'selectbox_values' => $cdnfsd_engine_values, + 'value' => $cdnfsd_engine, + 'disabled' => ( $is_pro ? null : true ), + 'description' => __( 'Select the CDN type you wish to use.', + 'w3-total-cache' ) . $cdnfsd_engine_extra_description + ) ); +?> +
diff --git a/Cdnfsd_Limelight_Api.php b/Cdnfsd_Limelight_Api.php new file mode 100644 index 0000000..777c4d1 --- /dev/null +++ b/Cdnfsd_Limelight_Api.php @@ -0,0 +1,101 @@ +url_base = 'https://purge.llnw.com/purge/v1/account/' . + $short_name . '/requests'; + $this->username = $username; + $this->api_key = $api_key; + } + + + + public function purge( $items ) { + $body = json_encode( array( 'patterns' => $items ) ); + return $this->_wp_remote_post( '', $body ); + } + + + + public function get( $uri ) { + return $this->_wp_remote_get( $uri ); + } + + + + private function _wp_remote_get( $uri, $body = '', $headers = array() ) { + $url = $this->url_base . $uri; + $headers = $this->_add_headers( $headers, $url, 'GET', $body ); + + $result = wp_remote_get( $url, array( + 'headers' => $headers, + 'body' => $body + ) ); + + return $this->_decode_response( $result ); + } + + + + private function _wp_remote_post( $uri, $body, $headers = array() ) { + $url = $this->url_base . $uri; + $headers = $this->_add_headers( $headers, $url, 'POST', $body ); + + $result = wp_remote_post( $url, array( + 'headers' => $headers, + 'body' => $body + ) ); + + return $this->_decode_response( $result ); + } + + + + private function _add_headers( $headers, $url, $method, $body ) { + $timestamp = '' . ( time() * 1000 ); + + $headers['Content-Type'] = 'application/json'; + $headers['X-LLNW-Security-Principal'] = $this->username; + $headers['X-LLNW-Security-Timestamp'] = $timestamp; + $headers['X-LLNW-Security-Token'] = hash_hmac( 'sha256', + $method . $url . $timestamp . $body, pack( 'H*', $this->api_key ) ); + + return $headers; + } + + + + private function _decode_response( $result ) { + if ( is_wp_error( $result ) ) + throw new \Exception( 'Failed to reach API endpoint' ); + + $response_json = @json_decode( $result['body'], true ); + if ( is_null( $response_json ) ) { + throw new \Exception( + 'Failed to reach API endpoint, got unexpected response ' . + $result['body'] ); + } + + if ( $result['response']['code'] != '200' && + $result['response']['code'] != '201' && + $result['response']['code'] != '202' && + $result['response']['code'] != '204' ) { + if ( isset( $response_json['errors'] ) && + isset( $response_json['errors'][0]['description'] ) ) { + throw new \Exception( $response_json['errors'][0]['description'] ); + } + + throw new \Exception( $result['body'] ); + } + + + return $response_json; + } +} diff --git a/Cdnfsd_Limelight_Engine.php b/Cdnfsd_Limelight_Engine.php new file mode 100644 index 0000000..6e9157c --- /dev/null +++ b/Cdnfsd_Limelight_Engine.php @@ -0,0 +1,85 @@ +short_name = $config['short_name']; + $this->username = $config['username']; + $this->api_key = $config['api_key']; + $this->debug = $config['debug']; + } + + + + function flush_urls( $urls ) { + if ( empty( $this->short_name ) || empty( $this->username ) || + empty( $this->api_key ) ) + throw new \Exception( __( 'Credentials are not specified.', 'w3-total-cache' ) ); + + $api = new Cdnfsd_Limelight_Api( $this->short_name, $this->username, $this->api_key ); + $items = array(); + + foreach ( $urls as $url ) { + $items[] = array( + 'pattern' => $url, + 'exact' => true, + 'evict' => false, + 'incqs' => false + ); + + // max number of items per request based on API docs + if ( count( $items ) >= 100 ) { + if ( $this->debug ) { + Util_Debug::log( 'cdnfsd', json_encode( $items, JSON_PRETTY_PRINT ) ); + } + + $api->purge( $items ); + $items = array(); + } + } + + if ( $this->debug ) { + Util_Debug::log( 'cdnfsd', json_encode( $items, JSON_PRETTY_PRINT ) ); + } + + $api->purge( $items ); + } + + + + /** + * Flushes CDN completely + */ + function flush_all() { + if ( empty( $this->short_name ) || empty( $this->username ) || + empty( $this->api_key ) ) + throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) ); + + $api = new Cdnfsd_Limelight_Api( $this->short_name, $this->username, $this->api_key ); + $url = Util_Environment::home_domain_root_url() . '/*'; + + $items = array( + array( + 'pattern' => $url, + 'exact' => false, + 'evict' => false, + 'incqs' => false + ) + ); + + if ( $this->debug ) { + Util_Debug::log( 'cdnfsd', json_encode( $items, JSON_PRETTY_PRINT ) ); + } + + $api->purge( $items ); + } +} diff --git a/Cdnfsd_Limelight_Page.php b/Cdnfsd_Limelight_Page.php new file mode 100644 index 0000000..62955b9 --- /dev/null +++ b/Cdnfsd_Limelight_Page.php @@ -0,0 +1,18 @@ +get_string( 'cdnfsd.limelight.api_key' ); +$authorized = !empty( $key ); + +?> +
+
+ + + + + + +
+ + + + + + + +
+ + +
+
diff --git a/Cdnfsd_Limelight_Popup.php b/Cdnfsd_Limelight_Popup.php new file mode 100644 index 0000000..b965260 --- /dev/null +++ b/Cdnfsd_Limelight_Popup.php @@ -0,0 +1,68 @@ +render_intro( array() ); + } + + + + private function render_intro( $details ) { + $config = Dispatcher::config(); + + include W3TC_DIR . '/Cdnfsd_Limelight_Popup_View_Intro.php'; + exit(); + } + + + + public function w3tc_ajax_cdnfsd_limelight_save() { + $short_name = $_REQUEST['short_name']; + $username = $_REQUEST['username']; + $api_key = $_REQUEST['api_key']; + + try { + $api = new Cdnfsd_Limelight_Api( $short_name, $username, $api_key ); + $url = Util_Environment::home_domain_root_url() . '/'; + + $items = array( + array( + 'pattern' => $url, + 'exact' => true, + 'evict' => false, + 'incqs' => false + ) + ); + + $api->purge( $items ); + } catch ( \Exception $ex ) { + $this->render_intro( array( + 'error_message' => 'Failed to make test purge request: ' . $ex->getMessage() + ) ); + exit(); + } + + $c = Dispatcher::config(); + $c->set( 'cdnfsd.limelight.short_name', $short_name ); + $c->set( 'cdnfsd.limelight.username', $username ); + $c->set( 'cdnfsd.limelight.api_key', $api_key ); + $c->save(); + + include W3TC_DIR . '/Cdnfsd_Limelight_Popup_View_Success.php'; + exit(); + } +} diff --git a/Cdnfsd_Limelight_Popup_View_Intro.php b/Cdnfsd_Limelight_Popup_View_Intro.php new file mode 100644 index 0000000..e02b9f9 --- /dev/null +++ b/Cdnfsd_Limelight_Popup_View_Intro.php @@ -0,0 +1,49 @@ + +
+ ' . $details['error_message'] . '
'; +?> +
+ + + + + + + + + + + + + + +
Account Short Name: + +
Username: + +
API Key: + +
+ +

+ +

+ +
+ diff --git a/Cdnfsd_Limelight_Popup_View_Success.php b/Cdnfsd_Limelight_Popup_View_Success.php new file mode 100644 index 0000000..83c5336 --- /dev/null +++ b/Cdnfsd_Limelight_Popup_View_Success.php @@ -0,0 +1,22 @@ + +
+
+ + +
+ Plugin was successfully configured to use this service.
+ Make sure you have updated domain DNS records. +

+ +

+ +
+ diff --git a/Cdn_MaxCdnFsd_Engine.php b/Cdnfsd_MaxCdn_Engine.php similarity index 97% rename from Cdn_MaxCdnFsd_Engine.php rename to Cdnfsd_MaxCdn_Engine.php index 336c725..ea2eac1 100644 --- a/Cdn_MaxCdnFsd_Engine.php +++ b/Cdnfsd_MaxCdn_Engine.php @@ -3,7 +3,7 @@ -class Cdn_MaxCdnFsd_Engine { +class Cdnfsd_MaxCdn_Engine { private $api_key; private $zone_id; diff --git a/Cdnfsd_MaxCdn_Page.php b/Cdnfsd_MaxCdn_Page.php new file mode 100644 index 0000000..2be0927 --- /dev/null +++ b/Cdnfsd_MaxCdn_Page.php @@ -0,0 +1,20 @@ +get_string( 'cdn.maxcdn_fsd.api_key' ); +$key = $config->get_string( 'cdnfsd.maxcdn.api_key' ); $authorized = !empty( $key ); -include W3TC_DIR . '/Cdn_Page_View_Header.php'; -?> -

- - -

-
- @@ -48,15 +40,15 @@ @@ -67,6 +59,3 @@ - -render_intro( array( - 'api_key' => $config->get_string( 'cdn.maxcdn_fsd.api_key' ) ) ); + 'api_key' => $config->get_string( 'cdnfsd.maxcdn.api_key' ) ) ); } @@ -42,7 +44,7 @@ private function render_intro( $details ) { 'w3tc_cdn_maxcdn_authorize' => 'y' ) ); - include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Intro.php'; + include W3TC_DIR . '/Cdnfsd_MaxCdn_Popup_View_Intro.php'; exit(); } @@ -81,7 +83,7 @@ public function w3tc_ajax_cdn_maxcdn_fsd_list_zones() { 'zones' => $zones ); - include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Zones.php'; + include W3TC_DIR . '/Cdnfsd_MaxCdn_Popup_View_Zones.php'; exit(); } @@ -115,7 +117,7 @@ public function w3tc_ajax_cdn_maxcdn_fsd_view_zone() { if ( empty( $zone_id ) ) { // create new zone mode $details['name'] = Util_Request::get( 'zone_new_name' ); - $details['ip']['new'] = Cdn_Fsd_Util::get_suggested_home_ip(); + $details['ip']['new'] = Cdnfsd_Util::get_suggested_home_ip(); } else { $api = \NetDNA::create( $api_key ); try { @@ -132,8 +134,8 @@ public function w3tc_ajax_cdn_maxcdn_fsd_view_zone() { $details['custom_domain']['current'] = ''; foreach ( $custom_domains as $d ) { - $details['custom_domain']['current'] = $d['custom_domain']; - if ( $d['custom_domain'] == Util_Environment::home_url_host() ) + $details['custom_domain']['current'] = $d; + if ( $d == Util_Environment::home_url_host() ) break; } @@ -143,7 +145,7 @@ public function w3tc_ajax_cdn_maxcdn_fsd_view_zone() { $details['url']['current'] = $zone['url']; $details['ip']['current'] = $zone['ip']; - $origin_ip = Cdn_Fsd_Util::get_suggested_home_ip(); + $origin_ip = Cdnfsd_Util::get_suggested_home_ip(); $cdn_ip = gethostbyname( $zone['tmp_url'] ); if ( $origin_ip != $cdn_ip ) @@ -152,7 +154,7 @@ public function w3tc_ajax_cdn_maxcdn_fsd_view_zone() { - include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Zone.php'; + include W3TC_DIR . '/Cdnfsd_MaxCdn_Popup_View_Zone.php'; exit(); } @@ -251,7 +253,7 @@ public function w3tc_ajax_cdn_maxcdn_fsd_configure_zone() { $added = false; foreach ( $custom_domains as $d ) { - if ( $d['custom_domain'] == $custom_domain ) { + if ( $d == $custom_domain ) { $added = true; break; } @@ -270,9 +272,9 @@ public function w3tc_ajax_cdn_maxcdn_fsd_configure_zone() { $zone_domain = $response['tmp_url']; $c = Dispatcher::config(); - $c->set( 'cdn.maxcdn_fsd.api_key', $api_key ); - $c->set( 'cdn.maxcdn_fsd.zone_id', $zone_id ); - $c->set( 'cdn.maxcdn_fsd.zone_domain', $zone_domain ); + $c->set( 'cdnfsd.maxcdn.api_key', $api_key ); + $c->set( 'cdnfsd.maxcdn.zone_id', $zone_id ); + $c->set( 'cdnfsd.maxcdn.zone_domain', $zone_domain ); $c->save(); $details = array( @@ -281,7 +283,43 @@ public function w3tc_ajax_cdn_maxcdn_fsd_configure_zone() { 'dns_cname_target' => $zone_domain, ); - include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Success.php'; + include W3TC_DIR . '/Cdnfsd_MaxCdn_Popup_View_Success.php'; + exit(); + } + + + + public function w3tc_ajax_cdn_maxcdn_fsd_configure_zone_skip() { + $api_key = $_REQUEST['api_key']; + $zone_id = Util_Request::get( 'zone_id', '' ); + + $api = \NetDNA::create( $api_key ); + + try { + $zone = $api->get_zone( $zone_id ); + } catch ( \Exception $ex ) { + $this->render_intro( array( + 'api_key' => $api_key, + 'error_message' => 'Failed to obtain custom domains: ' . $ex->getMessage() + ) ); + exit(); + } + + $zone_domain = $zone['cdn_url']; + + $c = Dispatcher::config(); + $c->set( 'cdnfsd.maxcdn.api_key', $api_key ); + $c->set( 'cdnfsd.maxcdn.zone_id', $zone_id ); + $c->set( 'cdnfsd.maxcdn.zone_domain', $zone_domain ); + $c->save(); + + $details = array( + 'name' => $zone['name'], + 'home_domain' => Util_Environment::home_url_host(), + 'dns_cname_target' => $zone_domain, + ); + + include W3TC_DIR . '/Cdnfsd_MaxCdn_Popup_View_Success.php'; exit(); } } diff --git a/Cdn_MaxCdnFsd_Popup_View_Intro.php b/Cdnfsd_MaxCdn_Popup_View_Intro.php similarity index 100% rename from Cdn_MaxCdnFsd_Popup_View_Intro.php rename to Cdnfsd_MaxCdn_Popup_View_Intro.php diff --git a/Cdn_MaxCdnFsd_Popup_View_Success.php b/Cdnfsd_MaxCdn_Popup_View_Success.php similarity index 100% rename from Cdn_MaxCdnFsd_Popup_View_Success.php rename to Cdnfsd_MaxCdn_Popup_View_Success.php diff --git a/Cdn_MaxCdnFsd_Popup_View_Zone.php b/Cdnfsd_MaxCdn_Popup_View_Zone.php similarity index 80% rename from Cdn_MaxCdnFsd_Popup_View_Zone.php rename to Cdnfsd_MaxCdn_Popup_View_Zone.php index d3a24e0..86363fe 100644 --- a/Cdn_MaxCdnFsd_Popup_View_Zone.php +++ b/Cdnfsd_MaxCdn_Popup_View_Zone.php @@ -37,10 +37,10 @@ - +
- + get_string( 'cdn.maxcdn_fsd.zone_domain' ) +echo $config->get_string( 'cdnfsd.maxcdn.zone_domain' ) ?>
This website domain has to be CNAME pointing to this - CDN domain + CDN domain
render_zone_boolean_change( $details, 'dns_check' ) ?>
CDN Domain:CDN Domain: render_zone_value_change( $details, 'custom_domain' ) ?>
- Domain CDN will handle + Domain CDN will handle
@@ -49,6 +49,10 @@ + +

diff --git a/Cdn_MaxCdnFsd_Popup_View_Zones.php b/Cdnfsd_MaxCdn_Popup_View_Zones.php similarity index 100% rename from Cdn_MaxCdnFsd_Popup_View_Zones.php rename to Cdnfsd_MaxCdn_Popup_View_Zones.php diff --git a/Cdnfsd_Page_View_Header.php b/Cdnfsd_Page_View_Header.php new file mode 100644 index 0000000..2f6faa3 --- /dev/null +++ b/Cdnfsd_Page_View_Header.php @@ -0,0 +1,15 @@ + + +

+ '.Cdnfsd_Util::engine_name( $config->get_string( 'cdnfsd.engine' ) ).'', + '' . __( 'enabled', 'w3-total-cache' ) : 'disabled">' . __( 'disabled', 'w3-total-cache' ) ) . '' +); ?> +

diff --git a/Cdnfsd_Plugin.php b/Cdnfsd_Plugin.php new file mode 100644 index 0000000..b0aeb77 --- /dev/null +++ b/Cdnfsd_Plugin.php @@ -0,0 +1,61 @@ +_config = Dispatcher::config(); + } + + /** + * Runs plugin + */ + function run() { + add_filter( 'w3tc_footer_comment', array( + $this, + 'w3tc_footer_comment' + ) ); + + add_action( 'w3tc_flush_all', array( + '\W3TC\Cdnfsd_CacheFlush', + 'w3tc_flush_all' + ), 3000, 1 ); + add_action( 'w3tc_flush_post', array( + '\W3TC\Cdnfsd_CacheFlush', + 'w3tc_flush_post' + ), 3000, 1 ); + add_action( 'w3tc_flushable_posts', '__return_true', 3000 ); + add_action( 'w3tc_flush_posts', array( + '\W3TC\Cdnfsd_CacheFlush', + 'w3tc_flush_all' + ), 3000 ); + add_action( 'w3tc_flush_url', array( + '\W3TC\Cdnfsd_CacheFlush', + 'w3tc_flush_url' + ), 3000, 1 ); + add_filter( 'w3tc_flush_execute_delayed_operations', array( + '\W3TC\Cdnfsd_CacheFlush', + 'w3tc_flush_execute_delayed_operations' + ), 3000 ); + + Util_AttachToActions::flush_posts_on_actions(); + } + + public function w3tc_footer_comment( $strings ) { + $config = Dispatcher::config(); + $via = $config->get_string('cdnfsd.engine'); + + $strings[] = sprintf( + __( 'Content Delivery Network Full Site Delivery via %s', 'w3-total-cache' ), + ( $via ? $via : 'N/A' ) ); + + return $strings; + } +} diff --git a/Cdnfsd_Plugin_Admin.php b/Cdnfsd_Plugin_Admin.php new file mode 100644 index 0000000..26776d3 --- /dev/null +++ b/Cdnfsd_Plugin_Admin.php @@ -0,0 +1,91 @@ +get_string( 'cdnfsd.engine' ); + + // attach to actions without firing class loading at all without need + if ( $cdnfsd_engine == 'cloudfront' ) { + add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( + '\W3TC\Cdnfsd_CloudFront_Page', + 'admin_print_scripts_performance_page_w3tc_cdn' ) ); + add_action( 'w3tc_ajax', array( + '\W3TC\Cdnfsd_CloudFront_Popup', + 'w3tc_ajax' ) ); + add_action( 'w3tc_settings_box_cdnfsd', array( + '\W3TC\Cdnfsd_CloudFront_Page', + 'w3tc_settings_box_cdnfsd' ) ); + } elseif ( $cdnfsd_engine == 'maxcdn' ) { + add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( + '\W3TC\Cdnfsd_MaxCdn_Page', + 'admin_print_scripts_performance_page_w3tc_cdn' ) ); + add_action( 'w3tc_ajax', array( + '\W3TC\Cdnfsd_MaxCdn_Popup', + 'w3tc_ajax' ) ); + add_action( 'w3tc_settings_box_cdnfsd', array( + '\W3TC\Cdnfsd_MaxCdn_Page', + 'w3tc_settings_box_cdnfsd' ) ); + } elseif ( $cdnfsd_engine == 'limelight' ) { + add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array( + '\W3TC\Cdnfsd_Limelight_Page', + 'admin_print_scripts_performance_page_w3tc_cdn' ) ); + add_action( 'w3tc_ajax', array( + '\W3TC\Cdnfsd_Limelight_Popup', + 'w3tc_ajax' ) ); + add_action( 'w3tc_settings_box_cdnfsd', array( + '\W3TC\Cdnfsd_Limelight_Page', + 'w3tc_settings_box_cdnfsd' ) ); + } + + add_action( 'w3tc_settings_general_boxarea_cdn_footer', + array( $this, 'w3tc_settings_general_boxarea_cdn_footer' ) ); + } + + + + public function w3tc_settings_general_boxarea_cdn_footer() { + $config = Dispatcher::config(); + + $cdnfsd_enabled = $config->get_boolean( 'cdnfsd.enabled' ); + $cdnfsd_engine = $config->get_string( 'cdnfsd.engine' ); + + $is_pro = Util_Environment::is_w3tc_pro( $config ); + + $cdnfsd_engine_values = array(); + $cdnfsd_engine_values[''] = array( + 'label' => __( 'Please select engine', 'w3-total-cache' ) + ); + $cdnfsd_engine_values['cloudfront'] = array( + 'label' => __( 'Amazon CloudFront', 'w3-total-cache' ), + ); + $cdnfsd_engine_values['cloudflare'] = array( + 'label' => __( 'CloudFlare (extension not activated)', 'w3-total-cache' ), + 'disabled' => true, + ); + $cdnfsd_engine_values['limelight'] = array( + 'label' => __( 'Limelight', 'w3-total-cache' ), + ); + $cdnfsd_engine_values['maxcdn'] = array( + 'label' => __( 'MaxCDN (recommended)', 'w3-total-cache' ), + ); + + $tag = ''; + if ( $cdnfsd_engine == 'cloudfront' ) { + $tag = '#cdn-fsd-cloudfront'; + } elseif ( $cdnfsd_engine == 'maxcdn' ) { + $tag = '#cdn-fsd-maxcdn'; + } + + if ( empty( $tag ) ) { + $cdnfsd_engine_extra_description = ''; + } else { + $cdnfsd_engine_extra_description = + ' See setup instructions'; + } + + include W3TC_DIR . '/Cdnfsd_GeneralPage_View.php'; + } +} diff --git a/Cdn_Fsd_Util.php b/Cdnfsd_Util.php similarity index 78% rename from Cdn_Fsd_Util.php rename to Cdnfsd_Util.php index aafb3cb..b3e8cc3 100644 --- a/Cdn_Fsd_Util.php +++ b/Cdnfsd_Util.php @@ -1,7 +1,15 @@ + * : Filename to import + */ + function import( $args = array(), $vars = array() ) { + $filename = array_shift( $args ); + + try { + $config = new Config(); + if ( !file_exists( $filename ) || !is_readable( $filename ) ) { + throw new \Exception( 'Cant read file: ' . $filename ); + } + if ( !$config->import( $filename ) ) { + throw new \Exception( 'import failed' ); + } + $config->save(); + } catch ( \Exception $e ) { + \WP_CLI::error( __( 'Config import failed: ' . $e->getMessage(), 'w3-total-cache' ) ); + } + + \WP_CLI::success( __( 'Configuration successfully imported.', 'w3-total-cache' ) ); + } + /** * Update query string for all static files */ diff --git a/Config.php b/Config.php index fb4acc0..9cfa260 100644 --- a/Config.php +++ b/Config.php @@ -29,7 +29,30 @@ class Config { * Reads config from file and returns it's content as array (or null) * Stored in this class to limit class loading */ - static public function util_array_from_file( $filename ) { + static public function util_array_from_storage( $blog_id, $preview ) { + if ( !defined( 'W3TC_CONFIG_CACHE_ENGINE' ) ) { + return self::_util_array_from_storage( $blog_id, $preview ); + } + + // config cache enabled + $config = ConfigCache::util_array_from_storage( $blog_id, $preview ); + if ( !is_null( $config ) ) { + return $config; + } + + $config = self::_util_array_from_storage( $blog_id, $preview ); + ConfigCache::save_item( $blog_id, $preview, $config ); + return $config; + } + + + + static private function _util_array_from_storage( $blog_id, $preview ) { + if ( defined( 'W3TC_CONFIG_DATABASE' ) && W3TC_CONFIG_DATABASE ) { + return ConfigDbStorage::util_array_from_storage( $blog_id, $preview ); + } + + $filename = self::util_config_filename( $blog_id, $preview ); if ( file_exists( $filename ) && is_readable( $filename ) ) { // including file directly instead of read+eval causes constant // problems with APC, ZendCache, and WSOD in a case of @@ -37,8 +60,9 @@ static public function util_array_from_file( $filename ) { $content = @file_get_contents( $filename ); $config = @json_decode( substr( $content, 14 ), true ); - if ( is_array( $config ) ) + if ( is_array( $config ) ) { return $config; + } } return null; @@ -61,20 +85,6 @@ static public function util_config_filename( $blog_id, $preview ) { - /* - * Returns config filename - * Stored in this class to limit class loading - * v<0.9.5 - */ - static public function util_config_filename_legacy_v1( $blog_id, $preview ) { - $postfix = ( $preview ? '-preview' : '' ) . '.php'; - - if ( $blog_id <= 0 ) - return W3TC_CONFIG_DIR . '/master' . $postfix; - else - return W3TC_CONFIG_DIR . '/' . sprintf( '%06d', $blog_id ) . $postfix; - } - /* * Returns config filename * Stored in this class to limit class loading @@ -228,6 +238,8 @@ public function set_extension_active_frontend( $extension, $this->set( 'extensions.active_frontend', $a ); } + + /** * Sets config value. * Method to override @@ -249,6 +261,8 @@ public function set( $key, $value ) { return $value; } + + /** * Check if we are in preview mode */ @@ -256,6 +270,8 @@ public function is_preview() { return $this->_preview; } + + /** * Returns true if we edit master config */ @@ -263,6 +279,8 @@ public function is_master() { return $this->_is_master; } + + public function is_compiled() { return $this->_compiled; } @@ -324,11 +342,20 @@ public function export() { */ public function import( $filename ) { if ( file_exists( $filename ) && is_readable( $filename ) ) { - $data = file_get_contents( $filename ); - $config = @json_decode( $data, true ); + $content = file_get_contents( $filename ); + if ( substr( $content, 0, 14 ) == '' ) { + $content = substr( $content, 14 ); + } - if ( is_array( $config ) ) { - foreach ( $config as $key => $value ) + $data = @json_decode( $content, true ); + if ( is_array( $data ) ) { + if ( !isset( $data['version'] ) || $data['version'] != W3TC_VERSION ) { + $c = new ConfigCompiler( $this->_blog_id, false ); + $c->load( $data ); + $data = $c->get_data(); + } + + foreach ( $data as $key => $value ) $this->set( $key, $value ); return true; @@ -355,17 +382,15 @@ public function get_md5() { * correctly */ public function load() { - $master_filename = Config::util_config_filename( 0, $this->_preview ); - $data = Config::util_array_from_file( $master_filename ); + $data = Config::util_array_from_storage( 0, $this->_preview ); // config file assumed is not up to date, use slow version if ( !isset( $data['version'] ) || $data['version'] != W3TC_VERSION ) return $this->load_full(); if ( !$this->is_master() ) { - $child_filename = Config::util_config_filename( $this->_blog_id, + $child_data = Config::util_array_from_storage( $this->_blog_id, $this->_preview ); - $child_data = Config::util_array_from_file( $child_filename ); if ( !is_null( $child_data ) ) { if ( !isset( $data['version'] ) || $data['version'] != W3TC_VERSION ) diff --git a/ConfigCache.php b/ConfigCache.php new file mode 100644 index 0000000..0971439 --- /dev/null +++ b/ConfigCache.php @@ -0,0 +1,108 @@ +get( self::get_key( $blog_id, $preview ) ); + if ( is_array( $config ) ) { + return $config; + } + + return null; + } + + + + /** + * Removes config cache entry so that it can be read from original source + * on next attempt + */ + static public function remove_item( $blog_id, $preview ) { + $cache = self::get_cache(); + + $cache->hard_delete( self::get_key( $blog_id, false ) ); + $cache->hard_delete( self::get_key( $blog_id, true ) ); + } + + + + static public function save_item( $blog_id, $preview, $data ) { + $cache = self::get_cache(); + + $cache->set( self::get_key( $blog_id, $preview ), $data ); + } + + + + static private function get_cache() { + static $cache = null; + + if ( !is_null( $cache ) ) { + return $cache; + } + + switch ( W3TC_CONFIG_CACHE_ENGINE ) { + case 'memcached': + $engineConfig = array( + 'servers' => explode( ',', W3TC_CONFIG_CACHE_MEMCACHED_SERVERS ), + 'persistent' => + ( defined( 'W3TC_CONFIG_CACHE_MEMCACHED_PERSISTENT' ) ? + W3TC_CONFIG_CACHE_MEMCACHED_PERSISTENT : true ), + 'aws_autodiscovery' => + ( defined( 'W3TC_CONFIG_CACHE_MEMCACHED_AWS_AUTODISCOVERY' ) ? + W3TC_CONFIG_CACHE_MEMCACHED_AWS_AUTODISCOVERY : false ), + 'username' => + ( defined( 'W3TC_CONFIG_CACHE_MEMCACHED_USERNAME' ) ? + W3TC_CONFIG_CACHE_MEMCACHED_USERNAME : '' ), + 'password' => + ( defined( 'W3TC_CONFIG_CACHE_MEMCACHED_PASSWORD' ) ? + W3TC_CONFIG_CACHE_MEMCACHED_PASSWORD : '' ), + 'key_version_mode' => 'disabled' + ); + break; + + case 'redis': + $engineConfig = array( + 'servers' => explode( ',', W3TC_CONFIG_CACHE_REDIS_SERVERS ), + 'persistent' => + ( defined( 'W3TC_CONFIG_CACHE_REDIS_PERSISTENT' ) ? + W3TC_CONFIG_CACHE_REDIS_PERSISTENT : true ), + 'dbid' => + ( defined( 'W3TC_CONFIG_CACHE_REDIS_DBID' ) ? + W3TC_CONFIG_CACHE_REDIS_DBID : 0 ), + 'password' => + ( defined( 'W3TC_CONFIG_CACHE_REDIS_PASSWORD' ) ? + W3TC_CONFIG_CACHE_REDIS_PASSWORD : '' ), + 'key_version_mode' => 'disabled' + ); + break; + + default: + $engineConfig = array(); + } + + $engineConfig['blog_id'] = '0'; + $engineConfig['module'] = 'config'; + $engineConfig['host'] = ''; + $engineConfig['instance_id'] = + ( defined( 'W3TC_INSTANCE_ID' ) ? W3TC_INSTANCE_ID : 0 ); + + $cache = Cache::instance( W3TC_CONFIG_CACHE_ENGINE, $engineConfig ); + return $cache; + } + + + + static private function get_key( $blog_id, $preview ) { + return 'w3tc_config_' . $blog_id . ( $preview ? '_preview' : '' ); + } +} diff --git a/ConfigCompiler.php b/ConfigCompiler.php index d814712..e4107d1 100644 --- a/ConfigCompiler.php +++ b/ConfigCompiler.php @@ -70,27 +70,6 @@ static public function child_key_sealed( $key, $master_data, $child_data ) { - /** - * Reads config from file and returns it's content as array (or null) - * Stored in this class to limit class loading - */ - static private function util_array_from_file_legacy_v1( $filename ) { - if ( file_exists( $filename ) && is_readable( $filename ) ) { - // including file directly instead of read+eval causes constant - // problems with APC, ZendCache, and WSOD in a case of - // broken config file - $content = @file_get_contents( $filename ); - $config = @eval( substr( $content, 5 ) ); - - if ( is_array( $config ) ) - return $config; - } - - return null; - } - - - /** * Reads config from file and returns it's content as array (or null) * Stored in this class to limit class loading @@ -128,14 +107,14 @@ public function __construct( $blog_id, $preview ) { - public function load() { + public function load( $data = null ) { // apply data from master config - $master_filename = Config::util_config_filename( 0, $this->_preview ); - $data = Config::util_array_from_file( $master_filename ); + if ( is_null( $data ) ) { + $data = Config::util_array_from_storage( 0, $this->_preview ); + } if ( is_null( $data ) && $this->_preview ) { // try to read production data when preview not available - $master_filename = Config::util_config_filename( 0, false ); - $data = Config::util_array_from_file( $master_filename ); + $data = Config::util_array_from_storage( 0, false ); } // try to get legacy v2 data @@ -145,13 +124,6 @@ public function load() { $data = self::util_array_from_file_legacy_v2( $master_filename ); } - // try to get legacy v1 data - if ( is_null( $data ) ) { - $master_filename = Config::util_config_filename_legacy_v1( 0, - $this->_preview ); - $data = self::util_array_from_file_legacy_v1( $master_filename ); - } - if ( is_array( $data ) ) { $data = $this->upgrade( $data ); foreach ( $data as $key => $value ) @@ -163,14 +135,12 @@ public function load() { // apply child config - $child_filename = Config::util_config_filename( $this->_blog_id, + $data = Config::util_array_from_storage( $this->_blog_id, $this->_preview ); - $data = Config::util_array_from_file( $child_filename ); if ( is_null( $data ) && $this->_preview ) { // try to read production data when preview not available - $child_filename = Config::util_config_filename( $this->_blog_id, + $data = Config::util_array_from_storage( $this->_blog_id, false ); - $data = Config::util_array_from_file( $child_filename ); } // try to get legacy v2 data @@ -180,13 +150,6 @@ public function load() { $data = self::util_array_from_file_legacy_v2( $child_filename ); } - // try to get legacy v1 data - if ( is_null( $data ) ) { - $child_filename = Config::util_config_filename_legacy_v1( - $this->_blog_id, $this->_preview ); - $data = self::util_array_from_file_legacy_v1( $child_filename ); - } - if ( is_array( $data ) ) { $data = $this->upgrade( $data ); foreach ( $data as $key => $value ) { @@ -231,14 +194,7 @@ public function save() { } } - $filename = Config::util_config_filename( $this->_blog_id, - $this->_preview ); - if ( defined( 'JSON_PRETTY_PRINT' ) ) - $config = json_encode( $data, JSON_PRETTY_PRINT ); - else // for older php versions - $config = json_encode( $data ); - - Util_File::file_put_contents_atomic( $filename, '' . $config ); + ConfigUtil::save_item( $this->_blog_id, $this->_preview, $data ); } @@ -276,6 +232,20 @@ private function upgrade( $file_data ) { } } + // + // changes in 0.9.6 + // + if ( !isset( $file_data['cdn.cors_header'] ) ) { + $file_data['cdn.cors_header'] = true; + } + if ( isset( $file_data['cdn.engine'] ) && $file_data['cdn.engine'] == 'netdna' ) { + $file_data['cdn.engine'] = 'maxcdn'; + $file_data['cdn.maxcdn.authorization_key'] = $file_data['cdn.netdna.authorization_key']; + $file_data['cdn.maxcdn.domain'] = $file_data['cdn.netdna.domain']; + $file_data['cdn.maxcdn.ssl'] = $file_data['cdn.netdna.ssl']; + $file_data['cdn.maxcdn.zone_id'] = $file_data['cdn.netdna.zone_id']; + } + // // changes in 0.9.5 // @@ -425,6 +395,40 @@ function_exists( 'get_option' ) ) { } } + // + // changes in 0.9.5.4 + // + if ( isset( $file_data['cdn.engine'] ) ) { + if ( $file_data['cdn.engine'] == 'maxcdn_fsd' ) { + $file_data['cdnfsd.engine'] = 'maxcdn'; + $file_data['cdnfsd.enabled'] = $file_data['cdn.enabled']; + + if ( isset( $file_data['cdn.maxcdn_fsd.api_key'] ) ) { + $file_data['cdnfsd.maxcdn.api_key'] = + $file_data['cdn.maxcdn_fsd.api_key']; + $file_data['cdnfsd.maxcdn.zone_id'] = + $file_data['cdn.maxcdn_fsd.zone_id']; + $file_data['cdnfsd.maxcdn.zone_domain'] = + $file_data['cdn.maxcdn_fsd.zone_domain']; + } + } + if ( $file_data['cdn.engine'] == 'cloudfront_fsd' ) { + $file_data['cdnfsd.engine'] = 'cloudfront'; + $file_data['cdnfsd.enabled'] = $file_data['cdn.enabled']; + + if ( isset( $file_data['cdn.cloudfront_fsd.access_key'] ) ) { + $file_data['cdnfsd.cloudfront.access_key'] = + $file_data['cdn.cloudfront_fsd.access_key']; + $file_data['cdnfsd.cloudfront.distribution_domain'] = + $file_data['cdn.cloudfront_fsd.distribution_domain']; + $file_data['cdnfsd.cloudfront.secret_key'] = + $file_data['cdn.cloudfront_fsd.secret_key']; + $file_data['cdnfsd.cloudfront.distribution_id'] = + $file_data['cdn.cloudfront_fsd.distribution_id']; + } + } + } + $file_data['version'] = W3TC_VERSION; return $file_data; diff --git a/ConfigDbStorage.php b/ConfigDbStorage.php new file mode 100644 index 0000000..230ffcb --- /dev/null +++ b/ConfigDbStorage.php @@ -0,0 +1,390 @@ +query( $wpdb->prepare( + "DELETE FROM $table WHERE option_name = %s", + $option_name ) ); + } + + + + /** + * Deploys the config file from a preview config file + * + * @param integer $direction +1: preview->production + * -1: production->preview + * @param boolean $remove_source remove source file + */ + static public function preview_production_copy( $blog_id, $direction ) { + if ( $direction > 0 ) { + $content = self::load_content( $blog_id, true ); + self::save_item( $blog_id, false, $content ); + } else { + $content = self::load_content( $blog_id, false ); + self::save_item( $blog_id, true, $content ); + } + } + + + + static public function save_item( $blog_id, $preview, $data ) { + if ( is_string( $data ) ) { + $config = $data; + } else { + $config = json_encode( $data ); + } + + $table = self::get_table( $blog_id ); + $option_name = self::get_option_name( $blog_id, $preview ); + + global $wpdb; + $is_exists = !is_null( self::load_content( $blog_id, $preview ) ); + + if ( $is_exists ) { + $wpdb->query( $wpdb->prepare( + "UPDATE $table SET option_value = %s WHERE option_name = %s", + $config, $option_name ) ); + } else { + $wpdb->query( $wpdb->prepare( + "INSERT INTO $table (option_name, option_value) VALUES (%s, %s)", + $option_name, $config ) ); + } + } + + + + static private function get_table( $blog_id ) { + if ( defined('W3TC_CONFIG_DATABASE_TABLE' ) ) { + $template = W3TC_CONFIG_DATABASE_TABLE; + } else { + global $table_prefix; + $template = $table_prefix . '{blog_id_prefix}options'; + } + + if ( $blog_id <= 0 ) + $blog_id_prefix = ''; + else + $blog_id_prefix = $blog_id . '_'; + + return str_replace('{blog_id_prefix}', $blog_id_prefix, $template); + } + + + + static private function get_option_name( $blog_id, $preview ) { + return 'w3tc_config_' . $blog_id . ( $preview ? '_preview' : '' ); + } + + + + static private function load_content( $blog_id, $preview ) { + $table = self::get_table( $blog_id ); + $option_name = self::get_option_name( $blog_id, $preview ); + + if ( isset( $GLOBALS['wpdb'] ) ) { + global $wpdb; + $row = $wpdb->get_row( $wpdb->prepare( + "SELECT option_value FROM $table WHERE option_name = %s LIMIT 1", + $option_name ) ); + } else { + $db = new _WpdbEssentials( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ); + + if ( $db->ready ) { + $row = $db->get_row( $db->prepare( + "SELECT option_value FROM $table WHERE option_name = %s LIMIT 1", + $option_name ) ); + } else { + error_log('Failed to load w3tc config'); + $row = null; + } + + // close connection immediately so that real pooled connection may be + // reused by later inialized wpdb object + $db->close(); + } + + if ( is_object( $row ) ) { + return $row->option_value; + } + + return null; + } +} + + + +class _WpdbEssentials { + public $last_error = ''; + public $num_rows = 0; + var $rows_affected = 0; + var $last_query; + var $last_result; + + + + public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) { + if ( function_exists( 'mysqli_connect' ) ) { + if ( defined( 'WP_USE_EXT_MYSQL' ) ) { + $this->use_mysqli = ! WP_USE_EXT_MYSQL; + } elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) { + $this->use_mysqli = true; + } elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) { + $this->use_mysqli = true; + } + } + + $this->dbuser = $dbuser; + $this->dbpassword = $dbpassword; + $this->dbname = $dbname; + $this->dbhost = $dbhost; + + $this->db_connect(); + } + + + public function db_connect( $allow_bail = true ) { + $this->is_mysql = true; + + /* + * Deprecated in 3.9+ when using MySQLi. No equivalent + * $new_link parameter exists for mysqli_* functions. + */ + $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true; + $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; + + if ( $this->use_mysqli ) { + $this->dbh = mysqli_init(); + + // mysqli_real_connect doesn't support the host param including a port or socket + // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file. + $port = null; + $socket = null; + $host = $this->dbhost; + $port_or_socket = strstr( $host, ':' ); + if ( ! empty( $port_or_socket ) ) { + $host = substr( $host, 0, strpos( $host, ':' ) ); + $port_or_socket = substr( $port_or_socket, 1 ); + if ( 0 !== strpos( $port_or_socket, '/' ) ) { + $port = intval( $port_or_socket ); + $maybe_socket = strstr( $port_or_socket, ':' ); + if ( ! empty( $maybe_socket ) ) { + $socket = substr( $maybe_socket, 1 ); + } + } else { + $socket = $port_or_socket; + } + } + + if ( WP_DEBUG ) { + mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags ); + } else { + @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags ); + } + + if ( $this->dbh->connect_errno ) { + $this->dbh = null; + $this->last_error = + 'Connection failed with ' . $this->dbh->connect_errno . ' error code'; + if ( WP_DEBUG ) { + echo $this->last_error; + } + } + } else { + if ( WP_DEBUG ) { + $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags ); + } else { + $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags ); + } + } + + if ( $this->dbh ) { + $this->has_connected = true; + $this->ready = true; + $this->select( $this->dbname, $this->dbh ); + } else { + if ( WP_DEBUG ) { + echo 'Failed to connect to mysql server'; + } + } + } + + + + public function select( $db, $dbh = null ) { + if ( $this->use_mysqli ) { + $success = mysqli_select_db( $dbh, $db ); + } else { + $success = mysql_select_db( $db, $dbh ); + } + if ( ! $success ) { + $this->ready = false; + if ( WP_DEBUG ) { + echo 'Failed to select database'; + } + } + } + + + + public function prepare( $query, $args ) { + $args = func_get_args(); + array_shift( $args ); + // If args were passed as an array (as in vsprintf), move them up + if ( isset( $args[0] ) && is_array($args[0]) ) + $args = $args[0]; + $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it + $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting + $query = preg_replace( '|(?_real_escape( $string ); + } + + + + function _real_escape( $string ) { + if ( $this->use_mysqli ) { + return mysqli_real_escape_string( $this->dbh, $string ); + } else { + return mysql_real_escape_string( $string, $this->dbh ); + } + } + + + + public function get_row( $query = null ) { + $y = 0; + + if ( $query ) { + $this->query( $query ); + } else { + return null; + } + + if ( !isset( $this->last_result[$y] ) ) + return null; + + return $this->last_result[$y] ? $this->last_result[$y] : null; + } + + + + public function query( $query ) { + if ( ! $this->ready ) { + return false; + } + $this->_do_query( $query ); + // If there is an error then take note of it. + if ( $this->use_mysqli ) { + if ( $this->dbh instanceof \mysqli ) { + $this->last_error = mysqli_error( $this->dbh ); + } else { + $this->last_error = 'query: Unable to retrieve the error message from MySQL'; + } + } else { + if ( is_resource( $this->dbh ) ) { + $this->last_error = mysql_error( $this->dbh ); + } else { + $this->last_error = 'query: Unable to retrieve the error message from MySQL'; + } + } + + if ( $this->last_error ) { + if ( WP_DEBUG ) { + echo $this->last_error; + } + return false; + } + + $num_rows = 0; + $this->last_result = array(); + if ( $this->use_mysqli && $this->result instanceof \mysqli_result ) { + while ( $row = mysqli_fetch_object( $this->result ) ) { + $this->last_result[$num_rows] = $row; + $num_rows++; + } + } elseif ( is_resource( $this->result ) ) { + while ( $row = mysql_fetch_object( $this->result ) ) { + $this->last_result[$num_rows] = $row; + $num_rows++; + } + } + + // Log number of rows the query returned + // and return number of rows selected + $this->num_rows = $num_rows; + $return_val = $num_rows; + + return $return_val; + } + + private function _do_query( $query ) { + if ( ! empty( $this->dbh ) && $this->use_mysqli ) { + $this->result = mysqli_query( $this->dbh, $query ); + } elseif ( ! empty( $this->dbh ) ) { + $this->result = mysql_query( $query, $this->dbh ); + } + } + + + + public function close() { + if ( ! $this->dbh ) { + return false; + } + + if ( $this->use_mysqli ) { + $closed = mysqli_close( $this->dbh ); + } else { + $closed = mysql_close( $this->dbh ); + } + + if ( $closed ) { + $this->dbh = null; + $this->ready = false; + $this->has_connected = false; + } + + return $closed; + } +} diff --git a/ConfigKeys.php b/ConfigKeys.php index 4e73b64..16510c2 100644 --- a/ConfigKeys.php +++ b/ConfigKeys.php @@ -341,6 +341,7 @@ 'type' => 'boolean', 'default' => false ), + // name backwards-compatible. in reality works for apache too 'pgcache.cache.nginx_handle_xml' => array( 'type' => 'boolean', 'default' => false @@ -430,6 +431,22 @@ 'index\.php' ) ), + 'pgcache.reject.categories' => array( + 'type' => 'array', + 'default' => array() + ), + 'pgcache.reject.tags' => array( + 'type' => 'array', + 'default' => array() + ), + 'pgcache.reject.authors' => array( + 'type' => 'array', + 'default' => array() + ), + 'pgcache.reject.custom' => array( + 'type' => 'array', + 'default' => array() + ), 'pgcache.reject.ua' => array( 'type' => 'array', 'default' => array() @@ -532,6 +549,38 @@ 'type' => 'boolean', 'default' => false ), + 'pgcache.cookiegroups.enabled' => array( + 'type' => 'boolean', + 'default' => false + ), + 'pgcache.cookiegroups.groups' => array( + 'type' => 'array', + 'default' => array( + 'mobile' => array( + 'enabled' => false, + 'cache' => true, + 'cookies' => array( + 'wptouch-pro-view=mobile', + 'wptouch-pro-cache-state=mobile' + ) + ), + 'loggedin' => array( + 'enabled' => false, + 'cache' => true, + 'cookies' => array( + 'wordpress_logged_in_.*' + ) + ), + 'subscribers' => array( + 'enabled' => false, + 'cache' => true, + 'cookies' => array( + 'role=subscriber', + 'role=member' + ) + ) + ) + ), 'stats.enabled' => array( 'type' => 'boolean', @@ -944,7 +993,7 @@ ), 'cdn.theme.files' => array( 'type' => 'string', - 'default' => '*.css;*.js;*.gif;*.png;*.jpg;*.ico;*.ttf;*.otf,*.woff,*.less' + 'default' => '*.css;*.js;*.gif;*.png;*.jpg;*.ico;*.ttf;*.otf;*.woff;*.woff2;*.less' ), 'cdn.minify.enable' => array( 'type' => 'boolean', @@ -997,6 +1046,14 @@ 'type' => 'boolean', 'default' => false ), + 'cdn.admin.media_library' => array( + 'type' => 'boolean', + 'default' => false + ), + 'cdn.cors_header' => array( + 'type' => 'boolean', + 'default' => true + ), 'cdn.ftp.host' => array( 'type' => 'string', @@ -1085,6 +1142,10 @@ 'type' => 'string', 'default' => '' ), + 'cdn.s3.bucket.location' => array( + 'type' => 'string', + 'default' => 'us-east-1' + ), 'cdn.s3.cname' => array( 'type' => 'array', 'default' => array() @@ -1111,6 +1172,10 @@ 'type' => 'string', 'default' => '' ), + 'cdn.cf.bucket.location' => array( + 'type' => 'string', + 'default' => 'us-east-1' + ), 'cdn.cf.id' => array( 'type' => 'string', 'default' => '' @@ -1228,34 +1293,6 @@ 'type' => 'string', 'default' => 'auto' ), - 'cdn.netdna.alias' => array( - 'type' => 'string', - 'default' => '' - ), - 'cdn.netdna.consumerkey' => array( - 'type' => 'string', - 'default' => '' - ), - 'cdn.netdna.consumersecret' => array( - 'type' => 'string', - 'default' => '' - ), - 'cdn.netdna.authorization_key' => array( - 'type' => 'string', - 'default' => '' - ), - 'cdn.netdna.domain' => array( - 'type' => 'array', - 'default' => array() - ), - 'cdn.netdna.ssl' => array( - 'type' => 'string', - 'default' => 'auto' - ), - 'cdn.netdna.zone_id' => array( - 'type' => 'integer', - 'default' => 0 - ), 'cdn.maxcdn.authorization_key' => array( 'type' => 'string', 'default' => '' @@ -1384,6 +1421,19 @@ 'type' => 'boolean', 'default' => false ), + 'cdnfsd.enabled' => array( + 'type' => 'boolean', + 'default' => false + ), + 'cdnfsd.engine' => array( + 'type' => 'string', + 'default' => '' + ), + 'cdnfsd.debug' => array( + 'type' => 'boolean', + 'default' => false + ), + 'varnish.configuration_overloaded' => array( 'type' => 'boolean', 'default' => false @@ -1438,7 +1488,7 @@ ), 'browsercache.cssjs.expires' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.cssjs.lifetime' => array( 'type' => 'integer', @@ -1446,7 +1496,7 @@ ), 'browsercache.cssjs.nocookies' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.cssjs.cache.control' => array( 'type' => 'boolean', @@ -1458,7 +1508,7 @@ ), 'browsercache.cssjs.etag' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.cssjs.w3tc' => array( 'type' => 'boolean', @@ -1498,7 +1548,7 @@ ), 'browsercache.html.etag' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.html.w3tc' => array( 'type' => 'boolean', @@ -1518,7 +1568,7 @@ ), 'browsercache.other.expires' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.other.lifetime' => array( 'type' => 'integer', @@ -1526,7 +1576,7 @@ ), 'browsercache.other.nocookies' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.other.cache.control' => array( 'type' => 'boolean', @@ -1538,7 +1588,7 @@ ), 'browsercache.other.etag' => array( 'type' => 'boolean', - 'default' => false + 'default' => true ), 'browsercache.other.w3tc' => array( 'type' => 'boolean', @@ -1735,17 +1785,13 @@ ), - 'common.edge' => array( - 'type' => 'boolean', - 'default' => false - ), 'common.support' => array( 'type' => 'string', 'default' => '' ), 'common.track_usage' => array( 'type' => 'boolean', - 'default' => true + 'default' => false ), 'common.tweeted' => array( 'type' => 'boolean', @@ -1775,6 +1821,10 @@ 'type' => 'string', 'default' => '' ), + 'widget.pagespeed.key.restrict.referrer' => array( + 'type' => 'string', + 'default' => '' + ), 'widget.pagespeed.show_in_admin_bar' => array( 'type' => 'boolean', 'default' => false diff --git a/ConfigState.php b/ConfigState.php index 1241ba2..d06c609 100644 --- a/ConfigState.php +++ b/ConfigState.php @@ -9,8 +9,6 @@ * common.install - time() of plugin installation * common.support_us_invitations - number of invitations to support us shown * common.next_support_us_invitation - time() of next support us invitation - * common.edge_invitations - * common.next_edge_invitation * common.hide_note_wp_content_permissions * common.hide_note_no_zlib * common.hide_note_zlib_output_compression diff --git a/ConfigUtil.php b/ConfigUtil.php new file mode 100644 index 0000000..bd98188 --- /dev/null +++ b/ConfigUtil.php @@ -0,0 +1,81 @@ +production + * -1: production->preview + * @param boolean $remove_source remove source file + */ + static public function preview_production_copy( $blog_id, $direction ) { + if ( defined( 'W3TC_CONFIG_DATABASE' ) && W3TC_CONFIG_DATABASE ) { + ConfigDbStorage::preview_production_copy( $blog_id, $direction ); + } else { + $preview_filename = Config::util_config_filename( $blog_id, true ); + $production_filename = Config::util_config_filename( $blog_id, false ); + + if ( $direction > 0 ) { + $src = $preview_filename; + $dest = $production_filename; + } else { + $src = $production_filename; + $dest = $preview_filename; + } + + if ( !@copy( $src, $dest ) ) { + Util_Activation::throw_on_write_error( $dest ); + } + } + + if ( defined( 'W3TC_CONFIG_CACHE_ENGINE' ) ) { + ConfigCache::remove_item( $blog_id, $preview ); + } + } + + + + static public function save_item( $blog_id, $preview, $data ) { + if ( defined( 'W3TC_CONFIG_DATABASE' ) && W3TC_CONFIG_DATABASE ) { + ConfigDbStorage::save_item( $blog_id, $preview, $data ); + } else { + $filename = Config::util_config_filename( $blog_id, $preview ); + if ( defined( 'JSON_PRETTY_PRINT' ) ) + $config = json_encode( $data, JSON_PRETTY_PRINT ); + else // for older php versions + $config = json_encode( $data ); + + Util_File::file_put_contents_atomic( $filename, '' . $config ); + } + + if ( defined( 'W3TC_CONFIG_CACHE_ENGINE' ) ) { + ConfigCache::remove_item( $blog_id, $preview ); + } + } +} diff --git a/DbCache_Environment.php b/DbCache_Environment.php index 2ea6c59..ebab167 100644 --- a/DbCache_Environment.php +++ b/DbCache_Environment.php @@ -114,7 +114,7 @@ private function create_addin() { if ( isset( $_GET['page'] ) ) $url = 'admin.php?page=' . $_GET['page'] . '&'; else - $url = basename( Util_Environment::remove_query( $_SERVER['REQUEST_URI'] ) ) . '?page=w3tc_dashboard&'; + $url = basename( Util_Environment::remove_query_all( $_SERVER['REQUEST_URI'] ) ) . '?page=w3tc_dashboard&'; $remove_url = Util_Ui::admin_url( $url . 'w3tc_default_remove_add_in=dbcache' ); throw new Util_WpFile_FilesystemOperationException( sprintf( __( 'The Database add-in file db.php is not a W3 Total Cache drop-in. @@ -151,6 +151,9 @@ public function db_installed() { * @return boolean */ public function db_check_old_add_in() { + if ( !$this->db_installed() ) + return false; + return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_DB ) ) && strstr( $script_data, 'w3_instance' ) !== false ); } @@ -161,6 +164,9 @@ public function db_check_old_add_in() { * @return boolean */ public function is_dbcache_add_in() { + if ( !$this->db_installed() ) + return false; + return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_DB ) ) && strstr( $script_data, 'DbCache_Wpdb' ) !== false ); } diff --git a/DbCache_Wpdb.php b/DbCache_Wpdb.php index bbd16ef..b4ea0ea 100644 --- a/DbCache_Wpdb.php +++ b/DbCache_Wpdb.php @@ -26,6 +26,11 @@ static function instance() { $processors[] = new DbCache_WpdbInjection_QueryCaching(); } if ( Util_Environment::is_dbcluster() ) { + // dbcluster use mysqli only since other is obsolete now + if ( !defined( 'WP_USE_EXT_MYSQL' ) ) { + define( 'WP_USE_EXT_MYSQL', false ); + } + $processors[] = new Enterprise_Dbcache_WpdbInjection_Cluster(); } @@ -49,9 +54,9 @@ static function instance() { return $instance; } - private $processor_number; + private $active_processor_number; private $active_processor; - private $active_processors; + private $processors; private $debug; private $request_time_start = 0; @@ -60,6 +65,9 @@ static function instance() { * @param boolean $call_default_constructor */ public function __construct( $processors = null ) { + // required to initialize $use_mysqli which is private + parent::__construct( '', '', '', '' ); + // cant force empty parameter list due to wp requirements if ( !is_array( $processors ) ) throw new Exception( 'called incorrectly, use instance()' ); @@ -130,6 +138,13 @@ function flush_cache() { return $v; } + function db_connect( $allow_bail = true ) { + if ( empty( $this->dbuser ) ) { + // skip connection - called from constructor + } else + return parent::db_connect( $allow_bail ); + } + /** * Initializes object after processors configured. Called from instance() only */ @@ -152,6 +167,10 @@ function query( $query ) { return $this->active_processor->query( $query ); } + function _escape( $data ) { + return $this->active_processor->_escape( $data ); + } + /** * Overriten logic of wp_db by processor. */ @@ -247,6 +266,10 @@ function default_query( $query ) { return parent::query( $query ); } + function default__escape( $data ) { + return parent::_escape( $data ); + } + /** * Default implementation, calls wp_db apropriate method */ @@ -397,6 +420,23 @@ function query( $query ) { } } + /** + * Calls underlying processor's aproptiate method of wp_db + */ + function _escape( $data ) { + $switched = $this->wpdb_mixin->switch_active_processor( 1 ); + + try { + $r = $this->wpdb_mixin->_escape( $data ); + + $this->wpdb_mixin->switch_active_processor( -$switched ); + return $r; + } catch ( \Exception $e ) { + $this->wpdb_mixin->switch_active_processor( -$switched ); + throw $e; + } + } + /** * Calls underlying processor's aproptiate method of wp_db */ diff --git a/DbCache_WpdbInjection.php b/DbCache_WpdbInjection.php index 8045d80..3044376 100644 --- a/DbCache_WpdbInjection.php +++ b/DbCache_WpdbInjection.php @@ -54,6 +54,14 @@ function query( $query ) { return $this->wpdb_mixin->default_query( $query ); } + /** + * Placeholder for apropriate wp_db method replacement. + * By default calls wp_db implementation + */ + function _escape( $data ) { + return $this->wpdb_mixin->default__escape( $data ); + } + /** * Placeholder for apropriate wp_db method replacement. * By default calls wp_db implementation diff --git a/DbCache_WpdbInjection_QueryCaching.php b/DbCache_WpdbInjection_QueryCaching.php index d3b33f7..999bea6 100644 --- a/DbCache_WpdbInjection_QueryCaching.php +++ b/DbCache_WpdbInjection_QueryCaching.php @@ -185,6 +185,10 @@ function query( $query ) { return $return_val; } + function _escape( $data ) { + return $this->next_injection->_escape( $data ); + } + /** * Initializes object, calls underlying processor */ @@ -612,9 +616,25 @@ private function _get_reject_reason_message( $key ) { } public function w3tc_footer_comment( $strings ) { + $reason = $this->get_reject_reason(); + $append = ( $reason ? sprintf( ' (%s)', $reason ) : '' ); + + if ( $this->query_hits ) { + $strings[] = sprintf( + __( 'Database Caching %d/%d queries in %.3f seconds using %s%s', 'w3-total-cache' ), + $this->query_hits, $this->query_total, $this->time_total, + Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ), + $append ); + } else { + $strings[] = sprintf( + __( 'Database Caching using %s%s', 'w3-total-cache' ), + Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ), + $append ); + } + if ( $this->debug ) { + $strings[] = ''; $strings[] = "Db cache debug info:"; - $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ) ); $strings[] = sprintf( "%s%d", str_pad( 'Total queries: ', 20 ), $this->query_total ); $strings[] = sprintf( "%s%d", str_pad( 'Cached queries: ', 20 ), $this->query_hits ); $strings[] = sprintf( "%s%.4f", str_pad( 'Total query time: ', 20 ), $this->time_total ); @@ -639,22 +659,8 @@ public function w3tc_footer_comment( $strings ) { trim( $query['query'] ) ); } } - } else { - $reason = $this->get_reject_reason(); - $append = ( $reason ? sprintf( ' (%s)', $reason ) : '' ); - - if ( $this->query_hits ) { - $strings[] = sprintf( - __( 'Database Caching %d/%d queries in %.3f seconds using %s%s', 'w3-total-cache' ), - $this->query_hits, $this->query_total, $this->time_total, - Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ), - $append ); - } else { - $strings[] = sprintf( - __( 'Database Caching using %s%s', 'w3-total-cache' ), - Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ), - $append ); - } + + $strings[] = ''; } return $strings; diff --git a/Dispatcher.php b/Dispatcher.php index df7daef..d69b3ba 100644 --- a/Dispatcher.php +++ b/Dispatcher.php @@ -152,7 +152,7 @@ static public function on_browsercache_rules_generation_for_section( $config, $c return ''; return Util_RuleSnippet::canonical_without_location( $cdnftp, - $add_header_rules ); + $add_header_rules, $config->get_boolean( 'cdn.cors_header') ); } /** @@ -211,19 +211,6 @@ static private function _should_canonical_be_generated( $config, $cdnftp ) { $cdn->headers_support() == W3TC_CDN_HEADER_MIRRORING ); } - /** - * Checks whether canonical should be generated or not by browsercache plugin - * - * @param Config $config - * @return string|null - */ - static public function allow_origin_generated_by( $config ) { - if ( $config->get_boolean( 'cdn.enabled' ) ) - return 'cdn'; - - return null; - } - /** * If BrowserCache should generate rules specific for CDN. Used with CDN FTP * diff --git a/Enterprise_CacheFlush_MakeSnsEvent.php b/Enterprise_CacheFlush_MakeSnsEvent.php index 71773d7..c76ce89 100644 --- a/Enterprise_CacheFlush_MakeSnsEvent.php +++ b/Enterprise_CacheFlush_MakeSnsEvent.php @@ -100,8 +100,11 @@ function opcache_flush_file( $filename ) { * @param unknown $post_id * @return boolean */ - function flush_post( $post_id ) { - return $this->_prepare_message( array( 'action' => 'flush_post', 'post_id' => $post_id ) ); + function flush_post( $post_id, $extras = null ) { + return $this->_prepare_message( array( + 'action' => 'flush_post', + 'post_id' => $post_id, + 'extras' => $extras ) ); } /** @@ -134,8 +137,11 @@ function flush_all( $extras ) { * @param string $url * @return boolean */ - function flush_url( $url ) { - return $this->_prepare_message( array( 'action' => 'flush_url', 'url' => $url ) ); + function flush_url( $url, $extras ) { + return $this->_prepare_message( array( + 'action' => 'flush_url', + 'url' => $url, + 'extras' => $extras ) ); } /** diff --git a/Enterprise_Dbcache_WpdbInjection_Cluster.php b/Enterprise_Dbcache_WpdbInjection_Cluster.php index e704337..f195a33 100644 --- a/Enterprise_Dbcache_WpdbInjection_Cluster.php +++ b/Enterprise_Dbcache_WpdbInjection_Cluster.php @@ -11,56 +11,56 @@ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection { * * @var bool */ - var $check_tcp_responsiveness = true; + public $check_tcp_responsiveness = true; /** * Minimum number of connections to try before bailing * * @var int */ - var $min_tries = 3; + public $min_tries = 3; /** * Whether to use mysqli_connect with persistence * * @var bool */ - var $persistent = false; + public $persistent = false; /** * Cache of tables-to-dataset mapping if blog_dataset callback defined * * @var array */ - var $_blog_to_dataset = array(); + private $_blog_to_dataset = array(); /** * Optional directory of callbacks to determine datasets from queries * * @var array */ - var $_callbacks = array(); + private $_callbacks = array(); /** * The multi-dimensional array of datasets and servers * * @var array */ - var $_cluster_servers = array(); + private $_cluster_servers = array(); /** * Zone where application runs * * @var array */ - var $_current_zone = array( 'name' => 'all', 'zone_priorities' => array( 'all' ) ); + private $_current_zone = array( 'name' => 'all', 'zone_priorities' => array( 'all' ) ); /** * Established connections * * @var array */ - var $_connections; + private $_connections; /** * After any SQL_CALC_FOUND_ROWS query, the query "SELECT FOUND_ROWS()" @@ -71,19 +71,19 @@ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection { * * @var resource */ - var $_last_found_rows_result; + private $_last_found_rows_result; /** * The last table that was queried * * @var string */ - var $_last_table; + private $_last_table; /** * Reject reason */ - var $_reject_reason = null; + private $_reject_reason = null; /** * Send Reads To Masters. This disables slave connections while true. @@ -91,7 +91,7 @@ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection { * * @var array */ - var $_send_reads_to_master = false; + private $_send_reads_to_master = false; /** @@ -99,21 +99,21 @@ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection { * * @var bool */ - var $use_master_in_backend = true; + public $use_master_in_backend = true; /** * Which charset to use for connections * * @var string */ - var $charset = null; + public $charset = null; /** * Which collate to use for connections * * @var string */ - var $collate = null; + public $collate = null; /** * Initializes object @@ -122,11 +122,13 @@ function initialize() { global $wpdb_cluster; $wpdb_cluster = $this; - if ( file_exists( WP_CONTENT_DIR . '/db-cluster-config.php' ) ) { + if ( isset( $GLOBALS['w3tc_dbcluster_config'] ) ) { + $this->apply_configuration( $GLOBALS['w3tc_dbcluster_config'] ); + } elseif ( file_exists( WP_CONTENT_DIR . '/db-cluster-config.php' ) ) { // The config file resides in WP_CONTENT_DIR require WP_CONTENT_DIR . '/db-cluster-config.php'; } else { - $this->_reject_reason = 'db-cluster-config.php configuration file not found, ' . + $this->_reject_reason = 'w3tc dbcluster configuration not found, ' . 'using single-server configuration'; $this->next_injection->initialize(); return; @@ -141,6 +143,47 @@ function initialize() { $this->init_charset(); } + + /** + * Applies configuration from array + */ + public function apply_configuration( $c ) { + if ( isset( $c['persistent'] ) ) { + $this->persistent = $c['persistent']; + } + if ( isset( $c['check_tcp_responsiveness'] ) ) { + $this->persistent = $c['check_tcp_responsiveness']; + } + if ( isset( $c['use_master_in_backend'] ) ) { + $this->persistent = $c['use_master_in_backend']; + } + if ( isset( $c['charset'] ) ) { + $this->persistent = $c['charset']; + } + if ( isset( $c['collate'] ) ) { + $this->persistent = $c['collate']; + } + + if ( isset( $c['filters'] ) && is_array( $c['filters'] ) ) { + foreach ($c['filters'] as $filter) { + $this->add_callback( $filter['function_to_add'], $filter['tag'] ); + } + } + + if ( isset( $c['databases'] ) && is_array( $c['databases'] ) ) { + foreach ($c['databases'] as $key => $db) { + $this->add_database( $db ); + } + } + + if ( isset( $c['zones'] ) && is_array( $c['zones'] ) ) { + foreach ($c['zones'] as $key => $zone) { + $zone['name'] = $key; + $this->add_zone( $zone ); + } + } + } + /** * Sets default charset and collate * If DB_CHARSET not set uses utf8 @@ -312,7 +355,7 @@ function db_connect( $query = '', $use_master = null ) { // Try to reuse an existing connection $dbh = $this->_db_connect_reuse_connection(); - if ( is_resource( $dbh ) ) { + if ( is_object( $dbh ) ) { $this->wpdb_mixin->dbh = $dbh; return $dbh; } @@ -338,6 +381,7 @@ function db_connect( $query = '', $use_master = null ) { } while ( count( $servers ) < $this->min_tries ); // Connect to a database server $success = false; + $errno = 0; $dbhname = $this->dbhname; foreach ( $servers as $zone_index ) { @@ -365,15 +409,24 @@ function db_connect( $query = '', $use_master = null ) { $dbh = null; if ( is_null( $tcp_responded ) || $tcp_responded ) { - $dbh = @mysqli_connect( - ($this->persistent ? 'p:' : '') . $host . ':' . $port, - $user, $password, true); + $dbh = mysqli_init(); + + $socket = null; + $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; + + if ( WP_DEBUG ) { + mysqli_real_connect( $dbh, $host, $user, $password, null, $port, $socket, $client_flags ); + } else { + @mysqli_real_connect( $dbh, $host, $user, $password, null, $port, $socket, $client_flags ); + } } $elapsed = $this->wpdb_mixin->timer_stop(); - if ( is_resource( $dbh ) ) { - if ( mysqli_select_db( $name, $dbh ) ) { + if ( !is_null( $dbh ) ) { + if ( $dbh->connect_errno ) { + $errno = $dbh->connect_errno; + } elseif ( mysqli_select_db( $dbh, $name ) ) { $this->_connections[$dbhname] = array( 'dbh' => $dbh, 'database_name' => $name ); @@ -388,7 +441,7 @@ function db_connect( $query = '', $use_master = null ) { return $this->db_connect( $query, true ); } - return $this->wpdb_mixin->bail( "Unable to connect to $host:$port to $operation table '{$this->wpdb_mixin->table}' ($dataset)" ); + return $this->wpdb_mixin->bail( "Unable to connect to $host:$port to $operation table '{$this->wpdb_mixin->table}' ($dataset) with $errno" ); } $dbh = $this->_connections[$dbhname]['dbh']; @@ -440,7 +493,7 @@ function _db_connect_reuse_connection() { $connection = & $this->_connections[$dbhname]; $dbh = $connection['dbh']; - if ( !is_resource( $dbh ) ) + if ( !is_object( $dbh ) ) return null; if ( !mysqli_ping( $dbh ) ) { @@ -466,13 +519,13 @@ function set_charset( $dbh, $charset = null, $collate = null ) { $collate = $this->wpdb_mixin->collate; if ( $this->has_cap( 'collation', $dbh ) && !empty( $charset ) ) { if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset', $dbh ) ) { - mysqli_set_charset( $charset, $dbh ); + mysqli_set_charset( $dbh, $charset ); $this->wpdb_mixin->real_escape = true; } else { $query = $this->wpdb_mixin->prepare( 'SET NAMES %s', $charset ); if ( !empty( $collate ) ) $query .= $this->wpdb_mixin->prepare( ' COLLATE %s', $collate ); - mysqli_query( $query, $dbh ); + mysqli_query( $dbh, $query ); } } } @@ -511,18 +564,18 @@ function query( $query ) { $elapsed = 0; } else { $this->db_connect( $query ); - if ( !is_resource( $this->wpdb_mixin->dbh ) ) + if ( !is_object( $this->wpdb_mixin->dbh ) ) return false; $this->wpdb_mixin->timer_start(); - $this->wpdb_mixin->result = mysqli_query( $query, $this->wpdb_mixin->dbh ); + $this->wpdb_mixin->result = mysqli_query( $this->wpdb_mixin->dbh, $query ); $elapsed = $this->wpdb_mixin->timer_stop(); ++$this->wpdb_mixin->num_queries; if ( preg_match( '/^\s*SELECT\s+SQL_CALC_FOUND_ROWS\s/i', $query ) ) { if ( false === strpos( $query, "NO_SELECT_FOUND_ROWS" ) ) { $this->wpdb_mixin->timer_start(); - $this->wpdb_mixin->_last_found_rows_result = mysqli_query( "SELECT FOUND_ROWS()", $this->wpdb_mixin->dbh ); + $this->wpdb_mixin->_last_found_rows_result = mysqli_query( $this->wpdb_mixin->dbh, "SELECT FOUND_ROWS()" ); $elapsed += $this->wpdb_mixin->timer_stop(); ++$this->wpdb_mixin->num_queries; $query .= "; SELECT FOUND_ROWS()"; @@ -566,8 +619,6 @@ function query( $query ) { $num_rows++; } - @mysqli_free_result( $this->wpdb_mixin->result ); - // Log number of rows the query returned $this->num_rows = $num_rows; @@ -576,6 +627,19 @@ function query( $query ) { } } + /** + * Usually it's called from inside other higher-level function + * so connection is available, but sometimes called directly. + * So connection should be initialized + **/ + function _escape( $data ) { + if ( !$this->wpdb_mixin->dbh ) { + $this->db_connect( 'SELECT * FROM nothing' ); + } + + return $this->wpdb_mixin->default__escape( $data ); + } + /** * Whether or not MySQL database is at least the required minimum version. * The additional argument allows the caller to check a specific database. @@ -633,14 +697,15 @@ function has_cap( $db_cap, $dbh_or_table = false ) { * @return false|string false on failure, version number on success */ function db_version( $dbh_or_table = false ) { + $dbh = null; if ( !$dbh_or_table && $this->wpdb_mixin->dbh ) $dbh = $this->wpdb_mixin->dbh; - elseif ( is_resource( $dbh_or_table ) ) + elseif ( is_object( $dbh_or_table ) ) $dbh = $dbh_or_table; else $dbh = $this->db_connect( "SELECT FROM $dbh_or_table $this->wpdb_mixin->users" ); - if ( $dbh ) + if ( !is_null( $dbh ) ) return preg_replace( '/[^0-9.].*/', '', mysqli_get_server_info( $dbh ) ); return false; } @@ -653,7 +718,7 @@ function db_version( $dbh_or_table = false ) { function _disconnect( $dbhname ) { if ( isset( $this->_connections[$dbhname] ) ) { $dbh = $this->_connections[$dbhname]['dbh']; - if ( is_resource( $dbh ) ) + if ( is_object( $dbh ) ) mysqli_close( $dbh ); unset( $this->_connections[$dbhname] ); @@ -773,7 +838,7 @@ function _run_callbacks( $group, $args = null ) { * @return resource mysql database connection */ function _db_connect_fallback() { - if ( is_resource( $this->wpdb_mixin->dbh ) ) + if ( is_object( $this->wpdb_mixin->dbh ) ) return $this->wpdb_mixin->dbh; if ( !defined( 'DB_HOST' ) || !defined( 'DB_USER' ) @@ -781,19 +846,27 @@ function _db_connect_fallback() { || !defined( 'DB_NAME' ) ) return $this->wpdb_mixin->bail( "We were unable to query because there was no database defined." ); - $this->wpdb_mixin->dbh = @mysqli_connect( - ( $this->persistent? 'p:': '' ) . DB_HOST, DB_USER, DB_PASSWORD, - true ); + $this->wpdb_mixin->dbh = mysqli_init(); + + $port = null; + $socket = null; + $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; + + if ( WP_DEBUG ) { + mysqli_real_connect( $this->wpdb_mixin->dbh, DB_HOST, DB_USER, DB_PASSWORD, null, $port, $socket, $client_flags ); + } else { + @mysqli_real_connect( $this->wpdb_mixin->dbh, $host, $user, $password, null, $port, $socket, $client_flags ); + } - if ( !is_resource( $this->wpdb_mixin->dbh ) ) + if ( !is_object( $this->wpdb_mixin->dbh ) ) return $this->wpdb_mixin->bail( "We were unable to connect to the database. (DB_HOST)" ); - if ( !mysqli_select_db( DB_NAME, $this->wpdb_mixin->dbh ) ) + if ( !mysqli_select_db( $this->wpdb_mixin->dbh, DB_NAME ) ) return $this->wpdb_mixin->bail( "We were unable to select the database." ); if ( !empty( $this->wpdb_mixin->charset ) ) { $collation_query = "SET NAMES '$this->wpdb_mixin->charset'"; if ( !empty( $this->wpdb_mixin->collate ) ) $collation_query .= " COLLATE '$this->wpdb_mixin->collate'"; - mysqli_query( $collation_query, $this->wpdb_mixin->dbh ); + mysqli_query( $this->wpdb_mixin->dbh, $collation_query ); } return $this->wpdb_mixin->dbh; @@ -813,4 +886,4 @@ function w3tc_footer_comment( $strings ) { public function w3tc_usage_statistics_of_request( $storage ) { } -} \ No newline at end of file +} diff --git a/Extension_Amp_Plugin.php b/Extension_Amp_Plugin.php index dea23ed..c7ca254 100644 --- a/Extension_Amp_Plugin.php +++ b/Extension_Amp_Plugin.php @@ -2,9 +2,9 @@ namespace W3TC; class Extension_Amp_Plugin { - function __construct() { - $is_amp_endpoint = null; - } + private $is_amp_endpoint = null; + + public function run() { add_filter( 'w3tc_minify_js_enable', @@ -67,10 +67,6 @@ public function x_flush_post_queued_urls( $queued_urls ) { } $queued_urls = array_merge( $queued_urls, $amp_urls ); - - $filename = Util_Debug::log_filename( 'pagecache' ); - file_put_contents( $filename, "\nstart\n" . implode("\n", $queued_urls), FILE_APPEND ); - return $queued_urls; } diff --git a/Extension_CloudFlare_Api.php b/Extension_CloudFlare_Api.php index 4589650..4f4e034 100644 --- a/Extension_CloudFlare_Api.php +++ b/Extension_CloudFlare_Api.php @@ -181,7 +181,7 @@ private function _wp_remote_request( $method, $url, $body = array() ) { if ( is_null( $response_json ) || !isset( $response_json['success'] ) ) { throw new \Exception( 'Failed to reach API endpoint, got unexpected response ' . - $result['body'] ); + str_replace( '<', '.', str_replace( '>', '.', $result['body'] ) ) ); } if ( !$response_json['success'] ) { diff --git a/Extension_CloudFlare_Cdn_Page_View.php b/Extension_CloudFlare_Cdn_Page_View.php new file mode 100644 index 0000000..109e7a4 --- /dev/null +++ b/Extension_CloudFlare_Cdn_Page_View.php @@ -0,0 +1,31 @@ + +
+
+ + + + + + +
+ + + Open Configuration Page +
+ + + +
+
diff --git a/Extension_CloudFlare_Page.php b/Extension_CloudFlare_Page.php index 9462a54..19ef2d3 100644 --- a/Extension_CloudFlare_Page.php +++ b/Extension_CloudFlare_Page.php @@ -3,8 +3,10 @@ class Extension_CloudFlare_Page { static public function admin_print_scripts_w3tc_extensions() { - if ( isset( $_REQUEST['extension'] ) && - $_REQUEST['extension'] == 'cloudflare' ) { + if ( ( isset( $_REQUEST['extension'] ) && + $_REQUEST['extension'] == 'cloudflare' ) || + ( isset( $_REQUEST['page'] ) && + $_REQUEST['page'] == 'w3tc_cdnfsd' ) ) { wp_enqueue_script( 'w3tc_extension_cloudflare', plugins_url( 'Extension_CloudFlare_Page_View.js', W3TC_FILE ), array( 'jquery' ), '1.0' ); @@ -13,6 +15,12 @@ static public function admin_print_scripts_w3tc_extensions() { + static public function w3tc_settings_box_cdnfsd() { + include W3TC_DIR . '/Extension_CloudFlare_Cdn_Page_View.php'; + } + + + static public function w3tc_extension_page_cloudflare() { $c = Dispatcher::config(); $api = Extension_CloudFlare_SettingsForUi::api(); diff --git a/Extension_CloudFlare_Plugin_Admin.php b/Extension_CloudFlare_Plugin_Admin.php index cf4559a..95b8ac3 100644 --- a/Extension_CloudFlare_Plugin_Admin.php +++ b/Extension_CloudFlare_Plugin_Admin.php @@ -9,7 +9,7 @@ class Extension_CloudFlare_Plugin_Admin { static public function w3tc_extensions( $extensions, $config ) { $current_user = wp_get_current_user(); - + $message = array(); $message[] = 'CloudFlare'; $cloudflare_signup_email = ''; @@ -64,6 +64,11 @@ function run() { $widget->init(); // modify settings page + add_filter( 'w3tc_ui_config_item_cdnfsd.enabled', + array( $this, 'w3tc_ui_config_item_cdnfsd_enabled' ) ); + add_filter( 'w3tc_ui_config_item_cdnfsd.engine', + array( $this, 'w3tc_ui_config_item_cdnfsd_engine' ) ); + add_filter( 'w3tc_settings_general_anchors', array( $this, 'w3tc_settings_general_anchors' ) ); add_action( 'w3tc_settings_general_boxarea_cloudflare', @@ -73,10 +78,6 @@ function run() { array( $this, 'action_cloudflare_api_request' ) ); // modify main menu - add_action( 'w3tc_extension_page_cloudflare', array( - '\W3TC\Extension_CloudFlare_Page', - 'w3tc_extension_page_cloudflare' - ) ); add_filter( 'w3tc_admin_bar_menu', array( $this, 'w3tc_admin_bar_menu' ) ); // dashboard @@ -85,13 +86,31 @@ function run() { add_filter( 'w3tc_admin_actions', array( $this, 'w3tc_admin_actions' ) ); // own settings page + add_action( 'w3tc_extension_page_cloudflare', array( + '\W3TC\Extension_CloudFlare_Page', + 'w3tc_extension_page_cloudflare' + ) ); add_action( 'admin_print_scripts-performance_page_w3tc_extensions', array( '\W3TC\Extension_CloudFlare_Page', 'admin_print_scripts_w3tc_extensions' ) ); + add_action( 'w3tc_ajax', array( '\W3TC\Extension_CloudFlare_Popup', 'w3tc_ajax' ) ); + $cdnfsd_engine = $c->get_string( 'cdnfsd.engine' ); + + if ( empty( $cdnfsd_engine ) || $cdnfsd_engine == 'cloudflare' ) { + add_action( 'w3tc_settings_box_cdnfsd', array( + '\W3TC\Extension_CloudFlare_Page', + 'w3tc_settings_box_cdnfsd' + ) ); + add_action( 'admin_print_scripts-performance_page_w3tc_cdn', + array( '\W3TC\Extension_CloudFlare_Page', + 'admin_print_scripts_w3tc_extensions' + ) ); + } + // add check to comments page add_filter( 'comment_row_actions', array( $this, 'comment_row_actions' ), 10, 2 ); @@ -119,7 +138,7 @@ function run() { - function admin_notices() { + public function admin_notices() { $plugins = get_plugins(); if ( array_key_exists( 'cloudflare/cloudflare.php', $plugins ) && $this->_config->get_boolean( 'notes.cloudflare_plugin' ) ) { @@ -289,6 +308,39 @@ public function w3tc_dashboard_actions( $actions ) { + public function w3tc_ui_config_item_cdnfsd_enabled( $a ) { + $c = Dispatcher::config(); + $cdnfsd_engine = $c->get_string( 'cdnfsd.engine' ); + + // overwrite behavior if controlled by extension + if ( empty( $cdnfsd_engine ) || $cdnfsd_engine == 'cloudflare' ) { + $a['value'] = true; + } + + return $a; + } + + + + public function w3tc_ui_config_item_cdnfsd_engine( $a ) { + $c = Dispatcher::config(); + $cdnfsd_engine = $c->get_string( 'cdnfsd.engine' ); + + // overwrite behavior if controlled by extension + if ( empty( $cdnfsd_engine ) || $cdnfsd_engine == 'cloudflare' ) { + $a['value'] = 'cloudflare'; + } + + if ( isset( $a['selectbox_values']['cloudflare'] ) ) { + $a['selectbox_values']['cloudflare']['label'] = 'CloudFlare'; + $a['selectbox_values']['cloudflare']['disabled'] = null; + } + + return $a; + } + + + public function w3tc_settings_general_anchors( $anchors ) { $anchors[] = array( 'id' => 'cloudflare', 'text' => 'CloudFlare' ); return $anchors; diff --git a/Extension_CloudFlare_Popup_View_Zones.php b/Extension_CloudFlare_Popup_View_Zones.php index e1e644b..c22c063 100644 --- a/Extension_CloudFlare_Popup_View_Zones.php +++ b/Extension_CloudFlare_Popup_View_Zones.php @@ -5,8 +5,8 @@ die(); ?>
- + echo Util_Ui::nonce_field( 'w3tc' ); ?> - ' . $details['error_message'] . '
'; ?> -
- - - - - - - -
Zone: - -
- -
- 1 ): - for ( $page = 1; $page <= $details['total_pages']; $page++ ): - if ( $page == $details['page']): - echo $page; - else: ?> - - -   - - -
+
+ + + + + + + + + +
Zone: + +
+ +
+ 1 ): + for ( $page = 1; $page <= $details['total_pages']; $page++ ): + if ( $page == $details['page']): + echo $page; + else: ?> + + +   + + +
-

- -

- -
+

+ +

+ +
diff --git a/Extension_CloudFlare_SettingsForUi.php b/Extension_CloudFlare_SettingsForUi.php index e7b66cd..6643496 100644 --- a/Extension_CloudFlare_SettingsForUi.php +++ b/Extension_CloudFlare_SettingsForUi.php @@ -89,8 +89,8 @@ static public function settings_set( $api ) { $current_value = $settings[$settings_key]['value']; // convert checkbox value to on/off - // excetion: rocket loader is not checkbox so contains real value - if ( $settings_key != 'rocket_loader' ) { + // exception: rocket loader, ssl is not checkbox so contains real value + if ( $settings_key != 'rocket_loader' && $settings_key != 'ssl' ) { if ( $current_value == 'on' || $current_value == 'off' ) { // it's boolean, so control is checkbox - convert it $value = ( $value == '0' ? 'off' : 'on' ); diff --git a/Extension_FragmentCache_Plugin_Admin.php b/Extension_FragmentCache_Plugin_Admin.php index 868adc8..4302f69 100644 --- a/Extension_FragmentCache_Plugin_Admin.php +++ b/Extension_FragmentCache_Plugin_Admin.php @@ -92,14 +92,13 @@ public function w3tc_extension_plugin_links( $links ) { public function w3tc_admin_menu( $menu ) { - $menu = array_merge( $menu, array( - 'w3tc_fragmentcache' => array( - 'page_title' => __( 'Fragment Cache', 'w3-total-cache' ), - 'menu_text' => '' . - __( 'Fragment Cache', 'w3-total-cache' ) . '', - 'visible_always' => false - ) - ) ); + $menu['w3tc_fragmentcache'] = array( + 'page_title' => __( 'Fragment Cache', 'w3-total-cache' ), + 'menu_text' => '' . + __( 'Fragment Cache', 'w3-total-cache' ) . '', + 'visible_always' => false, + 'order' => 1100 + ); return $menu; } diff --git a/Extension_FragmentCache_WpObjectCache.php b/Extension_FragmentCache_WpObjectCache.php index 9031de1..519a2da 100644 --- a/Extension_FragmentCache_WpObjectCache.php +++ b/Extension_FragmentCache_WpObjectCache.php @@ -571,15 +571,20 @@ function _can_cache() { * @return string */ public function w3tc_footer_comment( $strings ) { + $append = ( $this->cache_reject_reason != '' ? + sprintf( ' (%s)', $this->cache_reject_reason ) :'' ); + + $strings[] = sprintf( + __( 'Fragment Caching %d/%d fragments using %s%s', 'w3-total-cache' ), + $this->cache_hits, $this->cache_total, + Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ), + $append ); + if ( $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) ) ) { - $strings[] = "Fragment Cache debug info:"; - $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ) ); + $strings[] = ''; + $strings[] = 'Fragment Cache debug info:'; $strings[] = sprintf( "%s%s", str_pad( 'Caching: ', 20 ), ( $this->_caching ? 'enabled' : 'disabled' ) ); - if ( !$this->_caching ) { - $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), $this->cache_reject_reason ); - } - $strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total ); $strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits ); $strings[] = sprintf( "%s%d", str_pad( 'Cache misses: ', 20 ), $this->cache_misses ); @@ -609,15 +614,7 @@ public function w3tc_footer_comment( $strings ) { str_pad( ( $debug['group'] == 'transient' ? 'site' : 'network' ), 10, ' ', STR_PAD_LEFT ), $debug['id'] ); } - } else { - $append = ( $this->cache_reject_reason != '' ? - sprintf( ' (%s)', $this->cache_reject_reason ) :'' ); - - $strings[] = sprintf( - __( 'Fragment Caching %d/%d fragments using %s%s', 'w3-total-cache' ), - $this->cache_hits, $this->cache_total, - Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ), - $append ); + $strings[] = ''; } return $strings; diff --git a/Extension_NewRelic_Plugin.php b/Extension_NewRelic_Plugin.php index 0a19fd8..12974c7 100644 --- a/Extension_NewRelic_Plugin.php +++ b/Extension_NewRelic_Plugin.php @@ -70,22 +70,34 @@ public function w3tc_config_default_values( $default_values ) { function ob_callback_browser( $buffer ) { $core = Dispatcher::component( 'Extension_NewRelic_Core' ); $app = $core->get_effective_browser_application(); - if ( isset( $app['loader_script'] ) && !$this->_should_disable_auto_rum() ) { + if ( isset( $app['loader_script'] ) && $this->_can_add_tracker_script( $buffer ) ) { $buffer = preg_replace( '~]*)*>~Ui', '\\0' . $app['loader_script'], $buffer, 1 ); } + $buffer = str_replace('{w3tc_newrelic_reject_reason}', + ( $this->newrelic_reject_reason != '' ? sprintf( ' (%s)', $this->newrelic_reject_reason ) + : '' ), + $buffer ); + return $buffer; } function ob_callback_apm( $buffer ) { - if ( $this->_config->get_boolean( array( 'newrelic', 'include_rum' ) ) ) { - if ( ( $this->_config->get_boolean( 'browsercache.html.compression' ) || - $this->_config->get_string( 'pgcache.engine' ) == 'file_generic' ) && !$this->_should_disable_auto_rum() ) { + if ( !$this->_can_add_tracker_script( $buffer ) ) { + $this->disable_auto_rum(); + } else { + if ( $this->_config->get_boolean( array( 'newrelic', 'include_rum' ) ) ) { $buffer = preg_replace( '~]*)*>~Ui', '\\0' . \NewRelicWrapper::get_browser_timing_header(), $buffer, 1 ); $buffer = preg_replace( '~<\\/body>~', \NewRelicWrapper::get_browser_timing_footer() . '\\0', $buffer, 1 ); } } + + $buffer = str_replace('{w3tc_newrelic_reject_reason}', + ( $this->newrelic_reject_reason != '' ? sprintf( ' (%s)', $this->newrelic_reject_reason ) + : '' ), + $buffer ); + return $buffer; } @@ -103,12 +115,19 @@ function disable_auto_rum() { \NewRelicWrapper::disable_auto_rum(); } - function _should_disable_auto_rum() { + function _can_add_tracker_script( $buffer ) { + // + $v = ''; + if ( preg_match('~^\s*<\?xml[^>]*>\s*newrelic_reject_reason = __( 'XSL not tracked', 'w3-total-cache' ); + return false; + } + $reject_reason = apply_filters( 'w3tc_newrelic_should_disable_auto_rum', null ); if ( !empty( $reject_reason ) ) { $this->newrelic_reject_reason = __( 'rejected by filter: ', 'w3-total-cache' ) . $reject_reason; - return true; + return false; } @@ -118,7 +137,7 @@ function _should_disable_auto_rum() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { $this->newrelic_reject_reason = __( 'DOING_AJAX constant is defined', 'w3-total-cache' ); - return true; + return false; } @@ -128,7 +147,7 @@ function _should_disable_auto_rum() { if ( defined( 'DONOTAUTORUM' ) && DONOTAUTORUM ) { $this->newrelic_reject_reason = __( 'DONOTAUTORUM constant is defined', 'w3-total-cache' ); - return true; + return false; } /** @@ -139,10 +158,10 @@ function _should_disable_auto_rum() { $this->newrelic_reject_reason = __( 'logged in role is rejected', 'w3-total-cache' ); - return true; + return false; } - return false; + return true; } /** @@ -183,11 +202,9 @@ public function set_appname() { } public function w3tc_footer_comment( $strings ) { - $append = ( $this->newrelic_reject_reason != '' ) ? - sprintf( ' (%s)', $this->newrelic_reject_reason ) : ''; $strings[] = sprintf( __( "Application Monitoring using New Relic%s", 'w3-total-cache' ), - $append ); + '{w3tc_newrelic_reject_reason}' ); return $strings; } diff --git a/Extension_NewRelic_Plugin_Admin.php b/Extension_NewRelic_Plugin_Admin.php index 16f33aa..c7763f0 100644 --- a/Extension_NewRelic_Plugin_Admin.php +++ b/Extension_NewRelic_Plugin_Admin.php @@ -88,13 +88,12 @@ public function w3tc_admin_menu( $menu ) { $c = Dispatcher::config(); $monitoring_type = $c->get_string( array( 'newrelic', 'monitoring_type' ) ); if ( $monitoring_type == 'apm' ) { - $menu = array_merge( $menu, array( - 'w3tc_monitoring' => array( - 'page_title' => __( 'Monitoring', 'w3-total-cache' ), - 'menu_text' => __( 'Monitoring', 'w3-total-cache' ), - 'visible_always' => false - ) - ) ); + $menu['w3tc_monitoring'] = array( + 'page_title' => __( 'Monitoring', 'w3-total-cache' ), + 'menu_text' => __( 'Monitoring', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 1200 + ); } return $menu; diff --git a/Extension_NewRelic_Popup_View.js b/Extension_NewRelic_Popup_View.js index 8d1d251..158aa73 100644 --- a/Extension_NewRelic_Popup_View.js +++ b/Extension_NewRelic_Popup_View.js @@ -6,7 +6,7 @@ jQuery(function($) { close: '', width: 800, height: 400, - url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + '&w3tc_action=newrelic_popup', }); }) @@ -14,8 +14,8 @@ jQuery(function($) { .on('click', '.w3tcnr_list_applications', function() { - var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + - '&w3tc_action=newrelic_list_applications&api_key=' + + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=newrelic_list_applications&api_key=' + encodeURIComponent($('.w3tcnr_api_key').val()); W3tc_Lightbox.load(url); }) @@ -23,26 +23,26 @@ jQuery(function($) { .on('click', '.w3tcnr_apply_configuration', function() { - var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + '&w3tc_action=newrelic_apply_configuration'; $('.w3tcnr_form').find('input').each(function(i) { var name = $(this).attr('name'); var type = $(this).attr('type'); if (type == 'radio') { - if (!$(this).attr('checked')) + if (!$(this).prop('checked')) return; } if (name) - url += '&' + encodeURIComponent(name) + '=' + + url += '&' + encodeURIComponent(name) + '=' + encodeURIComponent($(this).val()); }); $('.w3tcnr_form').find('select').each(function(i) { var name = $(this).attr('name'); - url += '&' + encodeURIComponent(name) + '=' + + url += '&' + encodeURIComponent(name) + '=' + encodeURIComponent($(this).val()); }); W3tc_Lightbox.load(url); }); -}); \ No newline at end of file +}); diff --git a/Extension_Swarmify_Page.php b/Extension_Swarmify_Page.php index 0ce0a1a..385bb8b 100644 --- a/Extension_Swarmify_Page.php +++ b/Extension_Swarmify_Page.php @@ -3,14 +3,7 @@ -class Extension_Swarmify_Page extends Base_Page_Settings { - /** - * Current page - */ - protected $_page = 'w3tc_swarmify'; - - - +class Extension_Swarmify_Page { public function render_content() { $config = Dispatcher::config(); diff --git a/Extension_WordPressSeo_Plugin_Admin.php b/Extension_WordPressSeo_Plugin_Admin.php index aefc507..809d77b 100644 --- a/Extension_WordPressSeo_Plugin_Admin.php +++ b/Extension_WordPressSeo_Plugin_Admin.php @@ -3,23 +3,11 @@ class Extension_WordPressSeo_Plugin_Admin { function run() { - add_action( 'admin_init', array( $this, 'admin_init' ) ); add_filter( 'w3tc_extension_plugin_links_wordpress-seo', array( $this, 'remove_settings' ) ); add_action( 'w3tc_activate_extension_wordpress-seo', array( $this, 'activate' ) ); add_action( 'w3tc_deactivate_extension_wordpress-seo', array( $this, 'deactivate' ) ); } - public function admin_init() { - $config = Dispatcher::config(); - $groups = $config->get_array( 'mobile.rgroups' ); - if ( Util_Environment::is_w3tc_edge( $config ) && - isset( $groups['google'] ) && - sizeof( $groups['google']['agents'] ) == 1 && - $groups['google']['agents'][0] == 'googlebot' ) { - w3tc_delete_user_agent_group( 'google' ); - } - } - /** * * diff --git a/Extensions_Plugin_Admin.php b/Extensions_Plugin_Admin.php index da86159..1d993ad 100644 --- a/Extensions_Plugin_Admin.php +++ b/Extensions_Plugin_Admin.php @@ -99,14 +99,14 @@ function run() { * @return array */ public function w3tc_admin_menu( $menu ) { - $menu_item = array( - 'w3tc_extensions' => array( - 'page_title' => __( 'Extensions', 'w3-total-cache' ), - 'menu_text' => __( 'Extensions', 'w3-total-cache' ), - 'visible_always' => false - ) + $menu['w3tc_extensions'] = array( + 'page_title' => __( 'Extensions', 'w3-total-cache' ), + 'menu_text' => __( 'Extensions', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 1900 ); - return array_merge( $menu, $menu_item ); + + return $menu; } /** diff --git a/Generic_AdminActions_Config.php b/Generic_AdminActions_Config.php index 698d7fd..5d06ba0 100644 --- a/Generic_AdminActions_Config.php +++ b/Generic_AdminActions_Config.php @@ -89,7 +89,7 @@ function w3tc_config_reset() { * @return void */ function w3tc_config_preview_enable() { - $this->preview_production_copy( -1 ); + ConfigUtil::preview_production_copy( Util_Environment::blog_id(), -1 ); Util_Environment::set_preview( true ); Util_Admin::redirect( array( @@ -104,9 +104,7 @@ function w3tc_config_preview_enable() { */ function w3tc_config_preview_disable() { $blog_id = Util_Environment::blog_id(); - $preview_filename = Config::util_config_filename( $blog_id, true ); - @unlink( $preview_filename ); - + ConfigUtil::remove_item( $blog_id, true ); Util_Environment::set_preview( false ); Util_Admin::redirect( array( @@ -120,7 +118,7 @@ function w3tc_config_preview_disable() { * @return void */ function w3tc_config_preview_deploy() { - $this->preview_production_copy( 1 ); + ConfigUtil::preview_production_copy( Util_Environment::blog_id(), 1 ); Util_Environment::set_preview( false ); Util_Admin::redirect( array( @@ -130,34 +128,6 @@ function w3tc_config_preview_deploy() { - /** - * Deploys the config file from a preview config file - * - * @param integer $direction +1: preview->production - * -1: production->preview - * @param boolean $remove_source remove source file - */ - private function preview_production_copy( $direction = 1 ) { - $blog_id = Util_Environment::blog_id(); - - $preview_filename = Config::util_config_filename( $blog_id, true ); - $production_filename = Config::util_config_filename( $blog_id, false ); - - if ( $direction > 0 ) { - $src = $preview_filename; - $dest = $production_filename; - } else { - $src = $production_filename; - $dest = $preview_filename; - } - - if ( !@copy( $src, $dest ) ) { - Util_Activation::throw_on_write_error( $dest ); - } - } - - - /** * Save dbcluster config action * @@ -195,11 +165,11 @@ function w3tc_config_save_support_us() { $this->_config->set( 'common.tweeted', $tweeted ); if ( $track_usage ) $this->_config->set( 'common.track_usage', true ); + else + $this->_config->set( 'common.track_usage', false ); if ( $signmeup ) { - if ( Util_Environment::is_w3tc_enterprise( $this->_config ) ) - $license = 'enterprise'; - elseif ( Util_Environment::is_w3tc_pro( $this->_config ) ) + if ( Util_Environment::is_w3tc_pro( $this->_config ) ) $license = 'pro'; else $license = 'community'; diff --git a/Generic_AdminActions_Default.php b/Generic_AdminActions_Default.php index 1e6ea57..f68ede2 100644 --- a/Generic_AdminActions_Default.php +++ b/Generic_AdminActions_Default.php @@ -317,7 +317,8 @@ private function _w3tc_save_options_process() { } // todo: move to cdn module - if ( in_array( $engine = $this->_config->get_string( 'cdn.engine' ), array( 'netdna', 'maxcdn' ) ) ) { + $engine = $this->_config->get_string( 'cdn.engine' ); + if ( $engine == 'maxcdn' ) { require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php'; $keys = explode( '+', $this->_config->get_string( 'cdn.'.$engine.'.authorization_key' ) ); if ( sizeof( $keys ) == 3 ) { @@ -548,11 +549,13 @@ private function _w3tc_save_options_process() { break; case 'maxcdn': - $config->set( 'cdn.maxcdn.domain', $cdn_domains ); - break; + $v = $config->get( 'cdn.maxcdn.domain' ); + if ( isset( $v['http_default'] ) ) + $cdn_domains['http_default'] = $v['http_default']; + if ( isset( $v['https_default'] ) ) + $cdn_domains['https_default'] = $v['https_default']; - case 'netdna': - $config->set( 'cdn.netdna.domain', $cdn_domains ); + $config->set( 'cdn.maxcdn.domain', $cdn_domains ); break; case 'cotendo': @@ -740,7 +743,7 @@ function read_request( $config ) { array_map( 'stripslashes_deep', $request_value ); else $request_value = stripslashes( $request_value ); - if ( strpos( $request_key, 'memcached_servers' ) ) + if ( strpos( $request_key, 'memcached__servers' ) || strpos( $request_key, 'redis__servers' ) ) $request_value = explode( ',', $request_value ); $key = Util_Ui::config_key_from_http_name( $request_key ); diff --git a/Generic_AdminActions_EdgeMode.php b/Generic_AdminActions_EdgeMode.php deleted file mode 100644 index 62aa028..0000000 --- a/Generic_AdminActions_EdgeMode.php +++ /dev/null @@ -1,28 +0,0 @@ -_config = Dispatcher::config(); - } - - public function w3tc_edge_mode_enable() { - $this->_config->set( 'common.edge', true ); - $this->_config->set( 'common.track_usage', true ); - $this->_config->save(); - - Util_Admin::redirect( array( 'w3tc_note' => 'enabled_edge' ) ); - } - - public function w3tc_edge_mode_disable() { - $this->_config->set( 'common.edge', false ); - $this->_config->save(); - - Util_Admin::redirect( array( 'w3tc_note' => 'disabled_edge' ) ); - } -} diff --git a/Generic_AdminActions_Flush.php b/Generic_AdminActions_Flush.php index 274daa0..e33f70d 100644 --- a/Generic_AdminActions_Flush.php +++ b/Generic_AdminActions_Flush.php @@ -16,7 +16,7 @@ function __construct() { * @return void */ function w3tc_flush_all() { - w3tc_flush_all(); + w3tc_flush_all( array( 'ui_action' => 'flush_button' ) ); $this->_redirect_after_flush( 'flush_all' ); } diff --git a/Generic_AdminActions_Test.php b/Generic_AdminActions_Test.php index c765832..e779fcc 100644 --- a/Generic_AdminActions_Test.php +++ b/Generic_AdminActions_Test.php @@ -39,6 +39,8 @@ function w3tc_test_memcached() { */ function w3tc_test_redis() { $servers = Util_Request::get_array( 'servers' ); + $password = Util_Request::get_string('password', ''); + $dbid = Util_Request::get_integer( 'dbid', 0 ); if ( count( $servers ) <= 0 ) $success = false; @@ -48,7 +50,9 @@ function w3tc_test_redis() { foreach ( $servers as $server ) { @$cache = Cache::instance( 'redis', array( 'servers' => $server, - 'persistent' => false + 'persistent' => false, + 'password' => $password, + 'dbid' => $dbid ) ); if ( is_null( $cache ) ) $success = false; @@ -204,7 +208,9 @@ function w3tc_test_pagespeed_results() { $config = Dispatcher::config(); $key = $config->get_string( 'widget.pagespeed.key' ); - $w3_pagespeed = new PageSpeed_Api( $key ); + $ref = $config->get_string( 'widget.pagespeed.key.restrict.referrer' ); + + $w3_pagespeed = new PageSpeed_Api( $key, $ref ); $results = $w3_pagespeed->analyze( get_home_url() ); include W3TC_INC_POPUP_DIR . '/pagespeed_results.php'; diff --git a/Generic_ConfigLabels.php b/Generic_ConfigLabels.php index 88b7f41..95c83a3 100644 --- a/Generic_ConfigLabels.php +++ b/Generic_ConfigLabels.php @@ -11,6 +11,7 @@ public function config_labels( $config_labels ) { 'cluster.messagebus.sns.topic_arn' => __( 'Topic ID:', 'w3-total-cache' ), 'cluster.messagebus.debug' => __( 'Message Bus', 'w3-total-cache' ), 'widget.pagespeed.key' => __( 'Page Speed API Key:', 'w3-total-cache' ), + 'widget.pagespeed.key.restrict.referrer' => __( 'Key Restriction (Referrer):', 'w3-total-cache' ), 'common.force_master' => __( 'Use single network configuration file for all sites.', 'w3-total-cache' ), 'config.path' => __( 'Nginx server configuration file path', 'w3-total-cache' ), 'config.check' => __( 'Verify rewrite rules', 'w3-total-cache' ), diff --git a/Generic_Environment.php b/Generic_Environment.php index 8a7159e..9822452 100644 --- a/Generic_Environment.php +++ b/Generic_Environment.php @@ -22,20 +22,18 @@ function fix_on_wpadmin_request( $config, $force_all_checks ) { $this->add_index_to_folders(); if ( count( $exs->exceptions() ) <= 0 ) { - $f = file_exists( Config::util_config_filename( 0, false ) ); + // save actual version of config is it's built on legacy configs + $f = ConfigUtil::is_item_exists( 0, false ); $f2 = file_exists( Config::util_config_filename_legacy_v2( 0, false ) ); $c = Dispatcher::config_master(); if ( ( $f || $f2 ) && $c->is_compiled() ) { $c->save(); - $f = file_exists( Config::util_config_filename( 0, false ) ); + $f = ConfigUtil::is_item_exists( 0, false ); } if ( $f && $f2 ) @unlink( Config::util_config_filename_legacy_v2( 0, false ) ); - - if ( !$f && !$f2 && $config->get_integer( 'common.instance_id', 0 ) == 0 ) - $this->notify_no_config_present( $config, $exs ); } if ( count( $exs->exceptions() ) > 0 ) @@ -134,10 +132,13 @@ private function delete_required_files( $exs ) { private function create_required_folders( $exs ) { // folders that we create if not exists $directories = array( - W3TC_CACHE_DIR, - W3TC_CONFIG_DIR + W3TC_CACHE_DIR ); + if ( !(defined( 'W3TC_CONFIG_DATABASE' ) && W3TC_CONFIG_DATABASE ) ) { + $directories[] = W3TC_CONFIG_DIR; + } + foreach ( $directories as $directory ) { try{ Util_WpFile::create_writeable_folder( $directory, WP_CONTENT_DIR ); @@ -178,26 +179,6 @@ private function add_index_to_folders() { } } - /** - * Check config file - * - * @param Config $config - * @param Util_Environment_Exceptions $exs - */ - private function notify_no_config_present( $config, $exs ) { - $onclick = 'document.location.href=\'' . - addslashes( wp_nonce_url( - 'admin.php?page=w3tc_general&w3tc_save_options', 'w3tc' ) ) . - '\';'; - $button = ''; - - $exs->push( new Util_Environment_Exception( 'W3 Total Cache: ' . - 'Default settings are in use. The configuration file could ' . - 'not be read or doesn\'t exist. Please ' . $button . - ' to create the file.' ) ); - } - /** * Returns true if advanced-cache.php is installed * diff --git a/Generic_Faq.php b/Generic_Faq.php index 89e3a1a..e3cd5b2 100644 --- a/Generic_Faq.php +++ b/Generic_Faq.php @@ -31,8 +31,6 @@ static public function parse() { self::parse_file( $faq, 'faq', '', '' ); - if ( Util_Environment::is_w3tc_edge( $config ) ) - self::parse_file( $faq, 'faq-edge', 'edge', 'Edge: ' ); if ( Util_Environment::is_w3tc_pro( $config ) ) self::parse_file( $faq, 'faq-pro', 'pro', 'Pro: ' ); diff --git a/Generic_GeneralPage_View_ShowEdge.js b/Generic_GeneralPage_View_ShowEdge.js index f9a4268..8ece540 100644 --- a/Generic_GeneralPage_View_ShowEdge.js +++ b/Generic_GeneralPage_View_ShowEdge.js @@ -4,8 +4,8 @@ jQuery(function() { id:'w3tc-overlay', close: '', width: 800, - height: 210, - url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + height: 240, + url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + '&w3tc_action=generic_edge' }); -}); \ No newline at end of file +}); diff --git a/Generic_GeneralPage_View_ShowSupportUs.js b/Generic_GeneralPage_View_ShowSupportUs.js index 568fcdd..1897b40 100644 --- a/Generic_GeneralPage_View_ShowSupportUs.js +++ b/Generic_GeneralPage_View_ShowSupportUs.js @@ -4,7 +4,10 @@ jQuery(function() { close: '', width: 800, height: 445, - url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + - '&w3tc_action=generic_support_us' + url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce + + '&w3tc_action=generic_support_us', + callback: function(lightbox) { + jQuery(".palette-twitter").click(function() {jQuery("#tweeted").val("1");}); + } }); -}); \ No newline at end of file +}); diff --git a/Generic_Page_Dashboard_View.css b/Generic_Page_Dashboard_View.css index 923b765..54a7491 100644 --- a/Generic_Page_Dashboard_View.css +++ b/Generic_Page_Dashboard_View.css @@ -32,15 +32,15 @@ #w3tc-dashboard-widgets div.postbox, #w3tc-dashboard-widgets #postbox-container-3 .hndle, #w3tc-dashboard-widgets #postbox-container-3 .inside, -#w3tc-dashboard-widgets #postbox-container-3 .meta-box-sortables -#w3tc-dashboard-widgets #postbox-container-3 .widefat{ +#w3tc-dashboard-widgets #postbox-container-3 .meta-box-sortables, +#w3tc-dashboard-widgets #postbox-container-3 .widefat { background: none; } #w3tc-dashboard-widgets #postbox-container-3 .postbox, #w3tc-dashboard-widgets #postbox-container-3 .hndle, #w3tc-dashboard-widgets #postbox-container-3 .inside, -#w3tc-dashboard-widgets #postbox-container-3 .meta-box-sortables -#w3tc-dashboard-widgets #postbox-container-3 .widefat{ +#w3tc-dashboard-widgets #postbox-container-3 .meta-box-sortables, +#w3tc-dashboard-widgets #postbox-container-3 .widefat { border:none; } #w3tc-dashboard-widgets { @@ -94,13 +94,6 @@ background: url("pub/img/w3tc_google-logo.png") 0 3px no-repeat; } -.w3tc-widget-netdna-logo { - float: left; - width: 150px; - height: 35px; - background: url("pub/img/w3tc_netdna-logo.png") 0 5px no-repeat; -} - .w3tc-widget-maxcdn-logo { width: 150px; height: 35px; @@ -112,6 +105,34 @@ float:left; margin-right:10px; } + +@media only screen and (max-device-width: 480px) { + #w3tc-dashboard-widgets #postbox-container-left { + margin-right: 0px; + } + #w3tc-dashboard-widgets { + min-width: auto; + } + #postbox-container-right { + width: 100%; + white-space: nowrap; + float: left; + margin-left: 0px; + } + #w3tc-dashboard-widgets { + background: none; + } + #w3tc-dashboard-widgets #normal-sortables .postbox { + width: 100%; + } + .w3tc_generic_widgetservice_label { + display: inline; + } + #cdn_maxcdn_authorization_key { + width: 100%; + } +} + /** * HiDPI Displays */ @@ -159,13 +180,5 @@ only screen and ( min-resolution: 2dppx) { background-image: url("pub/img/w3tc_netdna-logo-retina.png"); background-size: 177px 90px; } - - .w3tc-widget-maxcdn-logo{ - float: left; - width: 180px; - height: 90px; - background-image: url("pub/img/w3tc_maxcdn-logo-retina.png"); - background-size: 468px 90px; - } } -*/ \ No newline at end of file +*/ diff --git a/Generic_Page_General.php b/Generic_Page_General.php index 96c8ead..66f6599 100644 --- a/Generic_Page_General.php +++ b/Generic_Page_General.php @@ -35,7 +35,6 @@ function view() { $varnish_enabled = $modules->is_enabled( 'varnish' ); $enabled = $modules->plugin_is_enabled(); - $enabled_checkbox = $modules->all_modules_enabled(); $check_rules = Util_Rule::can_check_rules(); $disc_enhanced_enabled = !( ! $check_rules || ( !$this->is_master() && Util_Environment::is_wpmu() && $config_master->get_string( 'pgcache.engine' ) != 'file_generic' ) ); diff --git a/Generic_Plugin.php b/Generic_Plugin.php index 1b1f54a..51b46c1 100644 --- a/Generic_Plugin.php +++ b/Generic_Plugin.php @@ -499,24 +499,25 @@ function ob_callback( $buffer ) { if ( Util_Environment::is_preview_mode() ) $buffer .= "\r\n"; - if ( $this->_config->get_string( 'common.support' ) != '' || - $this->_config->get_boolean( 'common.tweeted' ) ) { - $buffer .= sprintf( "\r\n", - Util_Content::escape_comment( $host ), $date ); - } else { - $strings = array(); - $strings = apply_filters( 'w3tc_footer_comment', $strings ); - - $buffer .= "\r\n", Util_Content::escape_comment( $host ), $date ); - } + $strings = array(); + + if ( $this->_config->get_string( 'common.support' ) == '' && + !$this->_config->get_boolean( 'common.tweeted' ) ) { + $strings[] = 'Performance optimized by W3 Total Cache. Learn more: https://www.w3-edge.com/products/'; + $strings[] = ''; + } + + $strings = apply_filters( 'w3tc_footer_comment', $strings ); + + if ( count( $strings ) ) { + $strings[] = ''; + $strings[] = sprintf( "Served from: %s @ %s by W3 Total Cache", + Util_Content::escape_comment( $host ), $date ); + + $buffer .= "\r\n"; + } $buffer = apply_filters( 'w3tc_process_content', $buffer ); } diff --git a/Generic_Plugin_Admin.php b/Generic_Plugin_Admin.php index 0b6d559..a245b87 100644 --- a/Generic_Plugin_Admin.php +++ b/Generic_Plugin_Admin.php @@ -67,7 +67,7 @@ function run() { if ( is_network_admin() ) { add_action( 'network_admin_menu', array( $this, - 'admin_menu' + 'network_admin_menu' ) ); add_filter( 'network_admin_plugin_action_links_' . W3TC_FILE, array( $this, @@ -329,14 +329,23 @@ function admin_head() { _admin_menu( 'manage_network_options' ); + } + + function admin_menu() { + $this->_admin_menu( 'manage_options' ); + } + /** * Admin menu * * @return void */ - function admin_menu() { + private function _admin_menu( $base_capability ) { $base_capability = apply_filters( 'w3tc_capability_menu', - 'manage_options' ); + $base_capability ); if ( current_user_can( $base_capability ) ) { $menus = Dispatcher::component( 'Root_AdminMenu' ); @@ -649,7 +658,7 @@ function admin_notices() { 'flush_minify' => __( 'Minify cache successfully emptied.', 'w3-total-cache' ), 'flush_browser_cache' => __( 'Media Query string has been successfully updated.', 'w3-total-cache' ), 'flush_varnish' => __( 'Varnish servers successfully purged.', 'w3-total-cache' ), - 'flush_cdn' => __( 'CDN was successfully purged.', 'w3-total-cache' ), + 'flush_cdn' => __( 'CDN was successfully purged.', 'w3-total-cache' ), 'support_request' => __( 'The support request has been successfully sent.', 'w3-total-cache' ), 'config_import' => __( 'Settings successfully imported.', 'w3-total-cache' ), 'config_reset' => __( 'Settings successfully restored.', 'w3-total-cache' ), diff --git a/Generic_Plugin_AdminCompatibility.php b/Generic_Plugin_AdminCompatibility.php index fda41bb..55274ba 100644 --- a/Generic_Plugin_AdminCompatibility.php +++ b/Generic_Plugin_AdminCompatibility.php @@ -24,8 +24,6 @@ function run() { add_action( 'admin_notices', array( $this, 'verify' ) ); add_action( 'network_admin_notices', array( $this, 'verify' ) ); } - - $this->_backwards_import(); } /** @@ -110,29 +108,4 @@ private function _custom_message( $plugins ) { } return sprintf( "

$message

    %s
", implode( '', $plugin_names ) ); } - - /** - * Handle importing changed configuration from older versions - */ - private function _backwards_import() { - if ( $this->_config->get_string( 'cdn.engine' ) == 'netdna' && $this->_config->get_string( 'cdn.netdna.authorization_key' ) == '' ) { - - $alias = $this->_config->get_string( 'cdn.netdna.alias' ); - $consumerkey = $this->_config->get_string( 'cdn.netdna.consumerkey' ); - $consumersecret = $this->_config->get_string( 'cdn.netdna.consumersecret' ); - if ( $alias && $consumerkey && $consumersecret ) { - $this->_config->set( 'cdn.maxcdn.authorization_key', "$alias+$consumerkey+$consumersecret" ); - $this->_config->set( 'cdn.netdna.authorization_key', "$alias+$consumerkey+$consumersecret" ); - $this->_config->set( 'cdn.engine', 'maxcdn' ); - $this->_config->set( 'cdn.maxcdn.zone_id', $this->_config->get_integer( 'cdn.netdna.zone_id', 0 ) ); - $this->_config->set( 'cdn.maxcdn.domain', $this->_config->get_array( 'cdn.netdna.domain' ) ); - $this->_config->set( 'cdn.maxcdn.ssl', $this->_config->get_string( 'cdn.netdna.ssl' ) ); - - try{ - $this->_config->save(); - $this->_config->refresh_cache(); - } catch ( \Exception $ex ) {} - } - } - } } diff --git a/Generic_Plugin_AdminNotifications.php b/Generic_Plugin_AdminNotifications.php index cac04e2..3379d0a 100644 --- a/Generic_Plugin_AdminNotifications.php +++ b/Generic_Plugin_AdminNotifications.php @@ -71,29 +71,6 @@ function admin_head() { do_action( 'w3tc_message_action_generic_support_us' ); } - - - // edge mode - $edge_reminder = !$support_reminder && - !Util_Environment::is_w3tc_edge( $this->_config ) && - $state->get_integer( 'common.edge_invitations' ) < 3 && - ( $state->get_integer( 'common.install' ) < - ( time() - W3TC_EDGE_TIMEOUT ) ) && - ( $state->get_integer( 'common.next_edge_invitation' ) < time() ); - - if ( $edge_reminder ) { - if ( $state->get_integer( 'common.edge_invitations' ) > 1 ) - $next = time() + 30 * 24 * 60 * 60; - else - $next = time() + W3TC_EDGE_TIMEOUT; - - $state->set( 'common.next_edge_invitation', $next ); - $state->set( 'common.edge_invitations', - $state->get_integer( 'common.edge_invitations' ) + 1 ); - $state->save(); - - do_action( 'w3tc_message_action_generic_edge' ); - } } /** @@ -144,10 +121,4 @@ public function w3tc_message_action_generic_edge() { plugins_url( 'Generic_GeneralPage_View_ShowEdge.js', W3TC_FILE ), array(), W3TC_VERSION ); } - - - - public function w3tc_ajax_generic_edge() { - include W3TC_INC_LIGHTBOX_DIR . '/edge.php'; - } } diff --git a/Minify_ContentMinifier.php b/Minify_ContentMinifier.php index c23767c..fca0368 100644 --- a/Minify_ContentMinifier.php +++ b/Minify_ContentMinifier.php @@ -31,7 +31,7 @@ class Minify_ContentMinifier { 'css' => array( 'Minify_CSS', 'minify' ), 'yuicss' => array( 'Minify_YUICompressor', 'minifyCss' ), - 'cssmin' => array( 'Minify0_CSSmin', 'minify' ), + 'cssmin' => array( 'w3tc_tubalmartin\CssMin\Minifier', 'minify' ), 'csstidy' => array( 'Minify_CSSTidy', 'minify' ), 'html' => array( 'Minify_HTML', 'minify' ), diff --git a/Minify_Plugin.php b/Minify_Plugin.php index 1d5c716..1f3f779 100644 --- a/Minify_Plugin.php +++ b/Minify_Plugin.php @@ -59,11 +59,10 @@ function run() { add_filter( 'w3tc_admin_bar_menu', array( $this, 'w3tc_admin_bar_menu' ) ); - if ( !$this->_config->get_boolean( 'minify.debug' ) ) - add_filter( 'w3tc_footer_comment', array( - $this, - 'w3tc_footer_comment' - ) ); + add_filter( 'w3tc_footer_comment', array( + $this, + 'w3tc_footer_comment' + ) ); if ( $this->_config->get_string( 'minify.engine' ) == 'file' ) { add_action( 'w3_minify_cleanup', array( @@ -425,21 +424,17 @@ function w3tc_footer_comment( $strings ) { : '' ) ); if ( $this->_config->get_boolean( 'minify.debug' ) ) { - $strings[] = "Minify debug info:"; - $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( 'minify.engine' ) ) ); + $strings[] = ''; + $strings[] = 'Minify debug info:'; $strings[] = sprintf( "%s%s", str_pad( 'Theme: ', 20 ), $this->get_theme() ); $strings[] = sprintf( "%s%s", str_pad( 'Template: ', 20 ), $this->get_template() ); - if ( $this->minify_reject_reason ) { - $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), $this->minify_reject_reason ); - } - if ( $this->error ) { $strings[] = sprintf( "%s%s", str_pad( 'Errors: ', 20 ), $this->error ); } if ( count( $this->replaced_styles ) ) { - $strings[] = "Replaced CSS files:"; + $strings[] = 'Replaced CSS files:'; foreach ( $this->replaced_styles as $index => $file ) { $strings[] = sprintf( "%d. %s", $index + 1, Util_Content::escape_comment( $file ) ); @@ -447,12 +442,13 @@ function w3tc_footer_comment( $strings ) { } if ( count( $this->replaced_scripts ) ) { - $strings[] = "Replaced JavaScript files:"; + $strings[] = 'Replaced JavaScript files:'; foreach ( $this->replaced_scripts as $index => $file ) { $strings[] = sprintf( "%d. %s\r\n", $index + 1, Util_Content::escape_comment( $file ) ); } } + $strings[] = ''; } return $strings; @@ -713,7 +709,6 @@ function get_template() { case ( is_author() && ( $template_file = get_author_template() ) ): case ( is_date() && ( $template_file = get_date_template() ) ): case ( is_archive() && ( $template_file = get_archive_template() ) ): - case ( is_comments_popup() && ( $template_file = get_comments_popup_template() ) ): case ( is_paged() && ( $template_file = get_paged_template() ) ): break; @@ -847,10 +842,14 @@ function get_style_custom( $files, $embed_to_html = false ) { if ( count( $files ) ) { if ( $embed_to_html ) { - $return['body'] = - $this->minify_helpers->get_minified_content_for_files( - $files, 'css' ); - } else { + $body = $this->minify_helpers->get_minified_content_for_files( + $files, 'css' ); + if ( !is_null( $body ) ) { + $return['body'] = $body; + } + } + + if ( empty( $return['body'] ) ) { $return['url'] = $this->minify_helpers->get_minify_url_for_files( $files, 'css' ); if ( !is_null( $return['url'] ) ) { @@ -1185,11 +1184,12 @@ function get_minified_content_for_files( $files, $type ) { $minify = Dispatcher::component( 'Minify_MinifiedFileRequestHandler' ); $m = $minify->process( $minify_filename, true ); - if ( isset( $m['content'] ) ) - $style = $m['content']; - else - $style = 'not set'; + if ( !isset( $m['content'] ) ) + return null; + if ( empty( $m['content'] ) ) + return null; + $style = $m['content']; return "\r\n"; } @@ -1485,7 +1485,9 @@ private function process_script_tag( $script_tag, $script_tag_number ) { if ( $tag_pos === false ) { // script is external but not found, skip processing it error_log( 'script not found:' . $script_tag ); - Minify_Core::log( 'script not found:' . $script_tag ); + if ( $this->debug ) { + Minify_Core::log( 'script not found:' . $script_tag ); + } return; } diff --git a/Minify_Plugin_Admin.php b/Minify_Plugin_Admin.php index 20fc0aa..0df51ee 100644 --- a/Minify_Plugin_Admin.php +++ b/Minify_Plugin_Admin.php @@ -90,9 +90,7 @@ public function w3tc_save_options( $data ) { public function admin_print_scripts_w3tc_general() { $state = Dispatcher::config_state(); - if ( !$state->get_boolean( 'minify.hide_minify_help' ) && - !Util_Environment::is_w3tc_edge( $this->_config ) && - !Util_Environment::is_w3tc_enterprise( $this->_config ) ) { + if ( !$state->get_boolean( 'minify.hide_minify_help' ) ) { wp_enqueue_script( 'w3tc-minify-help', plugins_url( 'Minify_GeneralPage_View_ShowHelp.js', W3TC_FILE ), array(), W3TC_VERSION ); diff --git a/ModuleStatus.php b/ModuleStatus.php index ee4351c..528d64b 100644 --- a/ModuleStatus.php +++ b/ModuleStatus.php @@ -56,22 +56,6 @@ public function is_running( $module ) { return apply_filters( "w3tc_module_is_running-{$module}", $this->is_enabled( $module ) ); } - /** - * - * - * @return bool - */ - public function all_modules_enabled() { - return $this->is_enabled( 'pgcache' ) - && $this->is_enabled( 'minify' ) - && $this->is_enabled( 'dbcache' ) - && $this->is_enabled( 'objectcache' ) - && $this->is_enabled( 'browsercache' ) - && $this->is_enabled( 'cdn' ) - && $this->is_enabled( 'varnish' ) - && $this->is_enabled( 'fragmentcache' ); - } - /** * * diff --git a/ObjectCache_Environment.php b/ObjectCache_Environment.php index 0d1f65b..f653970 100644 --- a/ObjectCache_Environment.php +++ b/ObjectCache_Environment.php @@ -115,7 +115,7 @@ private function create_addin() { if ( isset( $_GET['page'] ) ) $url = 'admin.php?page=' . $_GET['page'] . '&'; else - $url = basename( Util_Environment::remove_query( + $url = basename( Util_Environment::remove_query_all( $_SERVER['REQUEST_URI'] ) ) . '?page=w3tc_dashboard&'; $remove_url = Util_Ui::admin_url( $url . 'w3tc_default_remove_add_in=objectcache' ); @@ -155,6 +155,9 @@ public function objectcache_installed() { * @return boolean */ public function is_objectcache_old_add_in() { + if ( !$this->objectcache_installed() ) + return false; + return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_OBJECT_CACHE ) ) && ( ( strstr( $script_data, 'W3 Total Cache Object Cache' ) !== false ) || strstr( $script_data, 'w3_instance' ) !== false ) ); @@ -166,6 +169,9 @@ public function is_objectcache_old_add_in() { * @return boolean */ public function is_objectcache_add_in() { + if ( !$this->objectcache_installed() ) + return false; + return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_OBJECT_CACHE ) ) && strstr( $script_data, '//ObjectCache Version: 1.4' ) !== false ); } diff --git a/ObjectCache_WpObjectCache_Regular.php b/ObjectCache_WpObjectCache_Regular.php index 4e544b1..f474b1f 100644 --- a/ObjectCache_WpObjectCache_Regular.php +++ b/ObjectCache_WpObjectCache_Regular.php @@ -125,7 +125,8 @@ function __construct() { $this->_debug = $this->_config->get_boolean( 'objectcache.debug' ); $this->_caching = $_wp_using_ext_object_cache = $this->_can_cache(); $this->global_groups = $this->_config->get_array( 'objectcache.groups.global' ); - $this->nonpersistent_groups = $this->_config->get_array( 'objectcache.groups.nonpersistent' ); + $this->nonpersistent_groups = $this->_config->get_array( + 'objectcache.groups.nonpersistent' ); $this->_blog_id = Util_Environment::blog_id(); } @@ -143,10 +144,10 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { } $key = $this->_get_cache_key( $id, $group ); - $internal = isset( $this->cache[$key] ); + $in_incall_cache = isset( $this->cache[$key] ); $fallback_used = false; - if ( $internal && !$force ) { + if ( $in_incall_cache && !$force ) { $found = true; $value = $this->cache[$key]; } elseif ( $this->_caching && @@ -163,7 +164,7 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { json_encode($a); */ - if ( is_array( $v ) && $v['content'] != null ) { + if ( is_array( $v ) && isset( $v['content'] ) ) { $found = true; $value = $v['content']; } else { @@ -191,10 +192,12 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { $found = ( $value !== false ); } - $this->cache[$key] = $value; - $this->cache_total++; + if ( $found ) { + if ( !$in_incall_cache ) { + $this->cache[$key] = $value; + $this->cache_total++; + } - if ( $value !== false ) { $this->cache_hits++; } else { $this->cache_misses++; @@ -220,7 +223,7 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { if ( !$found ) $returned = 'not in cache'; else { - if ( $internal ) + if ( $in_incall_cache ) $returned = 'from in-call cache'; else $returned = 'from persistent cache'; @@ -412,8 +415,8 @@ function flush( $reason = '' ) { if ( $this->_debug ) { $this->debug_info[] = array( - 'id' => $id, - 'group' => $group, + 'id' => '', + 'group' => '', 'operation' => 'flush', 'returned' => $reason, 'data_size' => 0, @@ -540,7 +543,7 @@ private function _transient_fallback_get( $transient, $group ) { if ( ! isset( $value ) ) $value = get_site_option( $transient_option ); } else { - $value == false; + $value = false; } return $value; @@ -614,11 +617,14 @@ private function _transient_fallback_set( $transient, $value, $group, $expiratio function stats() { echo '

Summary

'; echo '

'; - echo 'Engine: ' . Cache::engine_name( $this->_config->get_string( 'objectcache.engine' ) ) . '
'; - echo 'Caching: ' . ( $this->_caching ? 'enabled' : 'disabled' ) . '
'; + echo 'Engine: ' . Cache::engine_name( + $this->_config->get_string( 'objectcache.engine' ) ) . '
'; + echo 'Caching: ' . + ( $this->_caching ? 'enabled' : 'disabled' ) . '
'; if ( !$this->_caching ) { - echo 'Reject reason: ' . $this->get_reject_reason() . '
'; + echo 'Reject reason: ' . + $this->get_reject_reason() . '
'; } echo 'Total calls: ' . $this->cache_total . '
'; @@ -631,13 +637,13 @@ function stats() { if ( $this->_debug ) { echo ''; - echo ''; + echo ''; foreach ( $this->debug_info as $index => $debug ) { echo ''; echo ''; - echo ''; - echo ''; + echo ''; + echo ''; echo ''; echo ''; echo ''; @@ -732,7 +738,8 @@ function _get_cache( $blog_id = null, $group = '' ) { case 'memcached': $engineConfig = array( 'servers' => $this->_config->get_array( 'objectcache.memcached.servers' ), - 'persistent' => $this->_config->get_boolean( 'objectcache.memcached.persistent' ), + 'persistent' => $this->_config->get_boolean( + 'objectcache.memcached.persistent' ), 'aws_autodiscovery' => $this->_config->get_boolean( 'objectcache.memcached.aws_autodiscovery' ), 'username' => $this->_config->get_string( 'objectcache.memcached.username' ), 'password' => $this->_config->get_string( 'objectcache.memcached.password' ) @@ -742,7 +749,8 @@ function _get_cache( $blog_id = null, $group = '' ) { case 'redis': $engineConfig = array( 'servers' => $this->_config->get_array( 'objectcache.redis.servers' ), - 'persistent' => $this->_config->get_boolean( 'objectcache.redis.persistent' ), + 'persistent' => $this->_config->get_boolean( + 'objectcache.redis.persistent' ), 'dbid' => $this->_config->get_integer( 'objectcache.redis.dbid' ), 'password' => $this->_config->get_string( 'objectcache.redis.password' ) ); @@ -824,7 +832,8 @@ function _check_can_cache_runtime( $group ) { $this->_can_cache_dynamic = true; } else { if ( $this->_caching ) { - if ( defined( 'WP_ADMIN' ) ) { + if ( defined( 'WP_ADMIN' ) && + ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) { $this->_can_cache_dynamic = false; $this->cache_reject_reason = 'WP_ADMIN defined'; return $this->_can_cache_dynamic; @@ -840,14 +849,20 @@ private function _is_transient_group( $group ) { } public function w3tc_footer_comment( $strings ) { - if ( $this->_config->get_boolean( 'objectcache.debug' ) ) { - $strings[] = "Object Cache debug info:"; - $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( 'objectcache.engine' ) ) ); - $strings[] = sprintf( "%s%s", str_pad( 'Caching: ', 20 ), ( $this->_caching ? 'enabled' : 'disabled' ) ); + $reason = $this->get_reject_reason(); + $append = ( $reason != '' ? sprintf( ' (%s)', $reason ) : '' ); - if ( !$this->_caching ) { - $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), $this->cache_reject_reason ); - } + $strings[] = sprintf( + __( 'Object Caching %d/%d objects using %s%s', 'w3-total-cache' ), + $this->cache_hits, $this->cache_total, + Cache::engine_name( $this->_config->get_string( 'objectcache.engine' ) ), + $append ); + + if ( $this->_config->get_boolean( 'objectcache.debug' ) ) { + $strings[] = ''; + $strings[] = 'Object Cache debug info:'; + $strings[] = sprintf( "%s%s", str_pad( 'Caching: ', 20 ), + ( $this->_caching ? 'enabled' : 'disabled' ) ); $strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total ); $strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits ); @@ -874,15 +889,7 @@ public function w3tc_footer_comment( $strings ) { str_pad( $debug['group'], 15, ' ', STR_PAD_LEFT ), $debug['id'] ); } - } else { - $reason = $this->get_reject_reason(); - $append = ( $reason != '' ? sprintf( ' (%s)', $reason ) : '' ); - - $strings[] = sprintf( - __( 'Object Caching %d/%d objects using %s%s', 'w3-total-cache' ), - $this->cache_hits, $this->cache_total, - Cache::engine_name( $this->_config->get_string( 'objectcache.engine' ) ), - $append ); + $strings[] = ''; } return $strings; diff --git a/PageSpeed_Api.php b/PageSpeed_Api.php index 0cd1c0e..65f409f 100644 --- a/PageSpeed_Api.php +++ b/PageSpeed_Api.php @@ -12,16 +12,20 @@ class PageSpeed_Api { /** * API Key - * - * @var string */ - var $key = ''; + private $key = ''; + + /** + * Referrer for key restricting + */ + private $key_restrict_referrer = ''; /** * PHP5-style constructor */ - function __construct( $api_key ) { + function __construct( $api_key, $api_ref ) { $this->key = $api_key; + $this->key_restrict_referrer = $api_ref; } /** @@ -51,7 +55,11 @@ function _request( $url ) { 'key' => $this->key, ) ); - $response = Util_Http::get( $request_url, array( 'timeout' => 120 ) ); + $response = Util_Http::get( $request_url, array( + 'timeout' => 120, + 'headers' => array( 'Referer' => $this->key_restrict_referrer ) + ) ); + if ( !is_wp_error( $response ) && $response['response']['code'] == 200 ) { return $response['body']; } diff --git a/PageSpeed_Plugin_Widget.php b/PageSpeed_Plugin_Widget.php index 972a9e5..a8647f1 100644 --- a/PageSpeed_Plugin_Widget.php +++ b/PageSpeed_Plugin_Widget.php @@ -91,8 +91,9 @@ public function w3tc_ajax_pagespeed_widgetdata() { $config = Dispatcher::config(); $key = $config->get_string( 'widget.pagespeed.key' ); + $ref = $config->get_string( 'widget.pagespeed.key.restrict.referrer' ); - $w3_pagespeed = new PageSpeed_Api( $key ); + $w3_pagespeed = new PageSpeed_Api( $key, $ref ); $r = $w3_pagespeed->analyze( get_home_url() ); if ( !$r ) { @@ -132,7 +133,8 @@ public function w3tc_monitoring_score( $score ) { $config = Dispatcher::config(); $key = $config->get_string( 'widget.pagespeed.key' ); - $w3_pagespeed = new PageSpeed_Api( $key ); + $ref = $config->get_string( 'widget.pagespeed.key.restrict.referrer' ); + $w3_pagespeed = new PageSpeed_Api( $key, $ref ); $r = $w3_pagespeed->analyze( $url ); diff --git a/PgCache_ConfigLabels.php b/PgCache_ConfigLabels.php index 46fdd51..06aacd9 100644 --- a/PgCache_ConfigLabels.php +++ b/PgCache_ConfigLabels.php @@ -47,6 +47,10 @@ public function config_labels( $config_labels ) { 'pgcache.reject.ua' => __( 'Rejected user agents:', 'w3-total-cache' ), 'pgcache.reject.cookie' => __( 'Rejected cookies:', 'w3-total-cache' ), 'pgcache.reject.uri' => __( 'Never cache the following pages:', 'w3-total-cache' ), + 'pgcache.reject.categories' => __( 'Never cache pages associated with these categories:', 'w3-total-cache' ), + 'pgcache.reject.tags' => __( 'Never cache pages that use these tags:', 'w3-total-cache' ), + 'pgcache.reject.authors' => __( 'Never cache pages by these authors:', 'w3-total-cache' ), + 'pgcache.reject.custom' => __( 'Never cache pages that use these custom fields:', 'w3-total-cache' ), 'pgcache.accept.files' => __( 'Cache exception list:', 'w3-total-cache' ), 'pgcache.accept.uri' => __( 'Non-trailing slash pages:', 'w3-total-cache' ), 'pgcache.cache.headers' => __( 'Specify page headers:', 'w3-total-cache' ), diff --git a/PgCache_ContentGrabber.php b/PgCache_ContentGrabber.php index 7a09cad..24df7b0 100644 --- a/PgCache_ContentGrabber.php +++ b/PgCache_ContentGrabber.php @@ -73,6 +73,7 @@ class PgCache_ContentGrabber { * @var string */ var $_page_key = ''; + private $_page_key_extension; /** * Shutdown buffer @@ -185,6 +186,14 @@ function process() { ( $this->_caching ? 'true' : 'false' ) ); } + $this->_page_key_extension = $this->_get_key_extension(); + if ( !$this->_page_key_extension['cache'] ) { + $this->_caching = false; + $this->cache_reject_reason = + $this->_page_key_extension['cache_reject_reason']; + } + + if ( $this->_caching && !$this->_late_caching ) { $this->_cached_data = $this->_extract_cached_page( false ); if ( $this->_cached_data ) { @@ -217,10 +226,10 @@ function process() { function _extract_cached_page( $with_filter ) { $cache = $this->_get_cache(); - $mobile_group = $this->_get_mobile_group(); - $referrer_group = $this->_get_referrer_group(); - $encryption = $this->_get_encryption(); - $compression = $this->_get_compression(); + $mobile_group = $this->_page_key_extension['useragent']; + $referrer_group = $this->_page_key_extension['referrer']; + $encryption = $this->_page_key_extension['encryption']; + $compression = $this->_page_key_extension['compression']; $group = ''; $sitemap_regex = $this->_config->get_string( 'pgcache.purge.sitemap_regex' ); @@ -234,8 +243,7 @@ function _extract_cached_page( $with_filter ) { /** * Check if page is cached */ - if ( !$this->_set_extract_page_key( $mobile_group, $referrer_group, - $encryption, $compression, '', $with_filter ) ) { + if ( !$this->_set_extract_page_key( $this->_page_key_extension, $with_filter ) ) { $data = null; } else { $data = $cache->get_with_old( $this->_page_key, $group ); @@ -246,8 +254,9 @@ function _extract_cached_page( $with_filter ) { * Try to get uncompressed version of cache */ if ( $compression && !$data ) { - if ( !$this->_set_extract_page_key( $mobile_group, - $referrer_group, $encryption, false, '', $with_filter ) ) { + if ( !$this->_set_extract_page_key( + array_merge( $this->_page_key_extension, + array( 'compression' => '') ), $with_filter ) ) { $data = null; } else { $data = $cache->get_with_old( $this->_page_key, $group ); @@ -271,17 +280,20 @@ function _extract_cached_page( $with_filter ) { - private function _set_extract_page_key( $mobile_group, $referrer_group, - $encryption, $compression, $content_type = '', $with_filter ) { - $this->_page_key = $this->_get_page_key( $mobile_group, $referrer_group, - $encryption, $compression, $content_type ); + private function _set_extract_page_key( $page_key_extension, $with_filter ) { + $this->_page_key = $this->_get_page_key( $page_key_extension ); if ( $with_filter ) { // return empty value if caching should not happen $this->_page_key = apply_filters( 'w3tc_page_extract_key', - $this->_page_key, $mobile_group, $referrer_group, - $encryption, $compression, $content_type, - $this->_request_host . $this->_request_uri ); + $this->_page_key, + $page_key_extension['useragent'], + $page_key_extension['referrer'], + $page_key_extension['encryption'], + $page_key_extension['compression'], + $page_key_extension['content_type'], + $this->_request_host . $this->_request_uri, + $page_key_extension ); } if ( !empty( $this->_page_key ) ) @@ -317,7 +329,7 @@ private function process_cached_page_and_exit( $data ) { // its last modification date is now, and any compression // browser wants cant be used, since its compressed now $time = time(); - $compression = $this->_get_compression(); + $compression = $this->_page_key_extension['compression']; } else { $time = $data['time']; $compression = $data['compression']; @@ -361,6 +373,7 @@ function ob_callback( $buffer ) { $can_cache = apply_filters( 'w3tc_can_cache', $original_can_cache, $this, $buffer ); if ( $can_cache != $original_can_cache ) $this->cache_reject_reason = 'Third-party plugin has modified caching activity'; + if ( $this->_debug ) { self::log( 'storing cached page: ' . ( $can_cache ? 'true' : 'false' ) . @@ -368,12 +381,17 @@ function ob_callback( $buffer ) { ' reason ' . $this->cache_reject_reason ); } + $buffer = str_replace('{w3tc_pagecache_reject_reason}', + ( $this->cache_reject_reason != '' ? sprintf( ' (%s)', $this->cache_reject_reason ) + : '' ), + $buffer ); + if ( $can_cache ) { $buffer = $this->_maybe_save_cached_result( $buffer, $has_dynamic ); } else { if ( $has_dynamic ) { // send common headers since output will be compressed - $compression_header = $this->_get_compression(); + $compression_header = $this->_page_key_extension['compression']; if ( defined( 'W3TC_PAGECACHE_OUTPUT_COMPRESSION_OFF' ) ) $compression_header = false; $headers = $this->_get_common_headers( $compression_header ); @@ -385,16 +403,15 @@ function ob_callback( $buffer ) { if ( $this->_old_exists ) { $cache = $this->_get_cache(); - $mobile_group = $this->_get_mobile_group(); - $referrer_group = $this->_get_referrer_group(); - $encryption = $this->_get_encryption(); + $mobile_group = $this->_page_key_extension['useragent']; + $referrer_group = $this->_page_key_extension['referrer']; + $encryption = $this->_page_key_extension['encryption']; $compressions_to_store = $this->_get_compressions(); - $content_type = ''; foreach ( $compressions_to_store as $_compression ) { - $_page_key = $this->_get_page_key( $mobile_group, - $referrer_group, $encryption, $_compression, - $content_type ); + $_page_key = $this->_get_page_key( + array_merge( $this->_page_key_extension, + array( 'compression' => $_compression ) ) ); $cache->hard_delete( $_page_key ); } } @@ -425,7 +442,7 @@ function ob_callback( $buffer ) { * @return void */ public function shutdown() { - $compression = $this->_get_compression(); + $compression = $this->_page_key_extension['compression']; // Parse dynamic content $buffer = $this->_parse_dynamic( $this->_shutdown_buffer ); @@ -459,10 +476,10 @@ private function _can_cache() { } if ( !$this->_config->get_boolean('pgcache.cache.ssl') && Util_Environment::is_https() ) { - $this->cache_reject_reason = 'SSL caching disabled'; + $this->cache_reject_reason = 'SSL caching disabled'; - return false; - } + return false; + } /** * Skip if posting @@ -498,7 +515,7 @@ private function _can_cache() { /** * Check request URI */ - if ( !in_array( $_SERVER['PHP_SELF'], $this->_config->get_array( 'pgcache.accept.files' ) ) && !$this->_check_request_uri() ) { + if ( !$this->_passed_accept_files() && !$this->_passed_reject_uri() ) { $this->cache_reject_reason = 'Requested URI is rejected'; return false; @@ -568,7 +585,6 @@ private function _can_cache2( $buffer ) { */ if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) { $this->cache_reject_reason = 'DONOTCACHEPAGE constant is defined'; - return false; } @@ -617,6 +633,39 @@ private function _can_cache2( $buffer ) { return false; } + if ( !$this->_passed_accept_files() ) { + if ( is_single() ) { + /** + * Don't cache pages associated with categories + */ + if ( $this->_passed_reject_categories() ) { + $this->cache_reject_reason = 'Page associated with a rejected category'; + return false; + } + /** + * Don't cache pages that use tags + */ + if ( $this->_passed_reject_tags() ) { + $this->cache_reject_reason = 'Page using a rejected tag'; + return false; + } + } + /** + * Don't cache pages by these authors + */ + if ( $this->_passed_reject_authors() ) { + $this->cache_reject_reason = 'Page written by a rejected author'; + return false; + } + /** + * Don't cache pages using custom fields + */ + if ( $this->_passed_reject_custom_fields() ) { + $this->cache_reject_reason = 'Page using a rejected custom field'; + return false; + } + } + return true; } @@ -741,7 +790,7 @@ function _get_cache() { * * @return boolean */ - function _check_request_uri() { + function _passed_reject_uri() { $auto_reject_uri = array( 'wp-login', 'wp-register' @@ -766,6 +815,100 @@ function _check_request_uri() { return true; } + /** + * Check if in the cache exception list + * + * @return boolean + */ + function _passed_accept_files() { + $accept_uri = $this->_config->get_array( 'pgcache.accept.files' ); + $accept_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $accept_uri ); + foreach ( $accept_uri as &$val ) $val = trim( str_replace( "~", "\~", $val ) ); + $accept_uri = array_filter( $accept_uri, function( $val ){ return $val != ""; } ); + if ( !empty( $accept_uri ) && @preg_match( '~' . implode( "|", $accept_uri ) . '~i', $this->_request_uri ) ) { + return true; + } + return false; + } + /** + * Checks page against rejected categories + * + * @return boolean + */ + function _passed_reject_categories() { + $reject_categories = $this->_config->get_array( 'pgcache.reject.categories' ); + if ( !empty( $reject_categories ) ) { + if ( $cats = get_the_category() ) { + foreach( $cats as $cat ) { + if ( in_array( $cat->slug, $reject_categories ) ) { + return true; + } + } + } + } + return false; + } + /** + * Checks page against rejected tags + * + * @return boolean + */ + function _passed_reject_tags() { + $reject_tags = $this->_config->get_array( 'pgcache.reject.tags' ); + if ( !empty( $reject_tags ) ) { + if ( $tags = get_the_tags() ) { + foreach( $tags as $tag ) { + if ( in_array( $tag->slug,$reject_tags ) ) { + return true; + } + } + } + } + return false; + } + /** + * Checks page against rejected authors + * + * @return boolean + */ + function _passed_reject_authors() { + $reject_authors = $this->_config->get_array( 'pgcache.reject.authors' ); + if ( !empty( $reject_authors ) ) { + if ( $author = get_the_author_meta( 'user_login' ) ) { + if ( in_array( $author, $reject_authors ) ) { + return true; + } + } + } + return false; + } + /** + * Checks page against rejected custom fields + * + * @return boolean + */ + function _passed_reject_custom_fields() { + $reject_custom = $this->_config->get_array( 'pgcache.reject.custom' ); + if ( empty( $reject_custom ) ) + return false; + + foreach ( $reject_custom as &$val ) { + $val = preg_quote( trim( $val ), '~' ); + } + $reject_custom = implode( '|', array_filter( $reject_custom ) ); + if ( !empty( $reject_custom ) ) { + if ( $customs = get_post_custom() ) { + foreach ( $customs as $key => $value ) { + if ( @preg_match( '~' . $reject_custom . '~i', $key . ( isset( $value[0] ) ? "={$value[0]}" : "" ) ) ) { + return true; + } + } + } + } + + return false; + } + /** * Checks User Agent * @@ -896,42 +1039,70 @@ function _compress( $data, $compression ) { } /** - * Returns current mobile group + * Returns page key extension for current request * * @return string */ - function _get_mobile_group() { - if ( $this->_mobile ) { - return $this->_mobile->get_group(); - } + private function _get_key_extension() { + $extension = array( + 'useragent' => '', + 'referrer' => '', + 'cookie' => '', + 'encryption' => '', + 'compression' => $this->_get_compression(), + 'content_type' => '', + 'cache' => true, + 'cache_reject_reason' => '', + ); - return ''; - } + if ( $this->_mobile ) + $extension['useragent'] = $this->_mobile->get_group(); + if ( $this->_referrer ) + $extension['referrer'] = $this->_referrer->get_group(); + if ( Util_Environment::is_https() ) + $extension['encryption'] = 'ssl'; - /** - * Returns current referrer group - * - * @return string - */ - function _get_referrer_group() { - if ( $this->_referrer ) { - return $this->_referrer->get_group(); - } + $this->_fill_key_extension_cookie( $extension ); - return ''; + return $extension; } - /** - * Returns current encryption - * - * @return string - */ - function _get_encryption() { - if ( Util_Environment::is_https() ) { - return 'ssl'; - } + private function _fill_key_extension_cookie( &$extension ) { + if ( !$this->_config->get_boolean( 'pgcache.cookiegroups.enabled' ) ) + return; - return ''; + $groups = $this->_config->get_array( 'pgcache.cookiegroups.groups' ); + foreach ( $groups as $group_name => $g ) { + if ( isset( $g['enabled'] ) && $g['enabled'] ) { + + $cookies = array(); + foreach ($g['cookies'] as $cookie ) { + $cookie = trim( $cookie ); + if ( !empty( $cookie ) ) { + $cookie = str_replace( '+', ' ', $cookie ); + $cookie = Util_Environment::preg_quote( $cookie ); + if ( strpos( $cookie, '=') === false ) + $cookie .= '=.*'; + $cookies[] = $cookie; + } + } + + if ( count( $cookies ) > 0 ) { + $cookies_regexp = '~^(' . implode( '|', $cookies ) . ')$~i'; + + foreach ( $_COOKIE as $key => $value ) { + if ( @preg_match( $cookies_regexp, $key . '=' . $value ) ) { + $extension['cookie'] = $group_name; + if ( !$g['cache'] ) { + $extension['cache'] = false; + $extension['cache_reject_reason'] = 'cookiegroup ' . $group_name; + } + return; + } + } + } + } + } } /** @@ -941,7 +1112,7 @@ function _get_encryption() { */ function _get_compression() { if ( $this->_debug ) // cannt generate/use compressed files during debug mode - return false; + return ''; if ( !Util_Environment::is_zlib_enabled() && !$this->_is_buggy_ie() ) { $compressions = $this->_get_compressions(); @@ -955,7 +1126,7 @@ function _get_compression() { } } - return false; + return ''; } /** @@ -1082,17 +1253,9 @@ function _get_cached_headers( $response_headers ) { /** * Returns page key * - * @param string $mobile_group - * @param string $referrer_group - * @param string $encryption - * @param string $compression - * @param string $content_type - * @param string $request_uri * @return string */ - function _get_page_key( $mobile_group = '', $referrer_group = '', - $encryption = '', $compression = '', $content_type = '', $request_url = '' ) { - + function _get_page_key( $page_key_extension, $request_url = '' ) { if ( $request_url ) { $parts = parse_url( $request_url ); $key = $parts['host'] . @@ -1135,48 +1298,40 @@ function _get_page_key( $mobile_group = '', $referrer_group = '', } /** - * Append mobile group + * Append extensions */ - if ( $mobile_group ) { - $key .= '_' . $mobile_group; - } - - /** - * Append referrer group - */ - if ( $referrer_group ) { - $key .= '_' . $referrer_group; - } - - /** - * Append encryption - */ - if ( $encryption ) { - $key .= '_' . $encryption; - } - - if ( Util_Environment::is_preview_mode() ) { + if ( !empty( $page_key_extension['useragent'] ) ) + $key .= '_' . $page_key_extension['useragent']; + if ( !empty( $page_key_extension['referrer'] ) ) + $key .= '_' . $page_key_extension['referrer']; + if ( !empty( $page_key_extension['cookie'] ) ) + $key .= '_' . $page_key_extension['cookie']; + if ( !empty( $page_key_extension['encryption'] ) ) + $key .= '_' . $page_key_extension['encryption']; + if ( Util_Environment::is_preview_mode() ) $key .= '_preview'; - } if ( $this->_enhanced_mode ) { - /** - * Append HTML extension. - * For nginx - we create .xml cache entries and redirect to them - */ - if ( Util_Environment::is_nginx() && substr( $content_type, 0, 8 ) == 'text/xml' && - $this->_config->get_boolean( 'pgcache.cache.nginx_handle_xml' ) ) - $key .= '.xml'; - else - $key .= '.html'; + $key_postfix = '.html'; + if ( $this->_config->get_boolean( 'pgcache.cache.nginx_handle_xml' ) ) { + $content_type = isset( $page_key_extension['content_type'] ) ? + $page_key_extension['content_type'] : ''; + + if ( @preg_match( "~(text/xml|text/xsl|application/rdf\+xml|application/rss\+xml|application/atom\+xml)~i", $content_type ) || + strpos( $this->_request_uri, "/feed/" ) !== false || + strpos( $this->_request_uri, ".xsl" ) !== false ) { + $key_postfix = '.xml'; + } + } + + $key .= $key_postfix; } /** * Append compression */ - if ( $compression ) { - $key .= '_' . $compression; - } + if ( $page_key_extension['compression'] ) + $key .= '_' . $page_key_extension['compression']; return $key; } @@ -1192,25 +1347,20 @@ function _get_page_key( $mobile_group = '', $referrer_group = '', */ public function w3tc_footer_comment( $strings ) { $strings[] = sprintf( - __( 'Page Caching using %s%s', 'w3-total-cache' ), + __( 'Page Caching using %s%s%s', 'w3-total-cache' ), Cache::engine_name( $this->_config->get_string( 'pgcache.engine' ) ), - ( $this->cache_reject_reason != '' - ? sprintf( ' (%s)', $this->cache_reject_reason ) - : '' ) ); + '{w3tc_pagecache_reject_reason}', + isset($this->_page_key_extension['cookie']) ? ' ' . $this->_page_key_extension['cookie'] : '' ); if ( $this->_debug ) { $time_total = Util_Debug::microtime() - $this->_time_start; $engine = $this->_config->get_string( 'pgcache.engine' ); - $strings[] = "Page cache debug info:"; + $strings[] = ''; + $strings[] = 'Page cache debug info:'; $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $engine ) ); $strings[] = sprintf( "%s%s", str_pad( 'Cache key: ', 20 ), $this->_page_key ); - if ( $this->cache_reject_reason != '' ) { - $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), - $this->cache_reject_reason ); - } - $strings[] = sprintf( "%s%.3fs", str_pad( 'Creation Time: ', 20 ), time() ); $headers = $this->_get_response_headers(); @@ -1224,6 +1374,8 @@ public function w3tc_footer_comment( $strings ) { Util_Content::escape_comment( $i['value'] ) ); } } + + $strings[] = ''; } return $strings; @@ -1256,7 +1408,7 @@ function _headers( $headers ) { } elseif ( $name == 'Status-Code' ) { if ( function_exists( 'http_response_code' ) ) // php5.3 compatibility) @http_response_code( $headers['Status-Code'] ); - } else { + } elseif ( !empty( $name ) && !empty( $value ) ) { @header( $name . ': ' . $value, !isset( $repeating[$name] ) ); $repeating[$name] = true; } @@ -1381,7 +1533,7 @@ function _get_common_headers( $compression ) { $vary = ''; //compressed && UAG - if ( $compression && $this->_get_mobile_group() ) { + if ( $compression && $this->_page_key_extension['useragent'] ) { $vary = 'Accept-Encoding,User-Agent,Cookie'; $headers['Content-Encoding'] = $compression; //compressed @@ -1389,7 +1541,7 @@ function _get_common_headers( $compression ) { $vary = 'Accept-Encoding'; $headers['Content-Encoding'] = $compression; //uncompressed && UAG - } elseif ( $this->_get_mobile_group() ) { + } elseif ( $this->_page_key_extension['useragent'] ) { $vary = 'User-Agent,Cookie'; } @@ -1579,19 +1731,18 @@ private function _is_cacheable_content_type() { $headers = headers_list(); foreach ( $headers as $header ) { $header = strtolower( $header ); - if ( stripos( $header, 'content-type' ) !== false ) { - $temp = explode( ';', $header ); - $temp = array_shift( $temp ); - $temp = explode( ':', $temp ); - $content_type = trim( $temp[1] ); + $m = null; + if ( preg_match( '~\s*content-type\s*:([^;]+)~', $header, $m ) ) { + $content_type = trim( $m[1] ); } } $cache_headers = apply_filters( 'w3tc_is_cacheable_content_type', array( '' /* redirects, they have only Location header set */, - 'application/json', 'text/html', 'text/xml', - 'application/xhtml+xml' + 'application/json', 'text/html', 'text/xml', 'text/xsl', + 'application/xhtml+xml', 'application/rss+xml', + 'application/atom+xml', 'application/rdf+xml' ) ); return in_array( $content_type, $cache_headers ); @@ -1599,10 +1750,25 @@ private function _is_cacheable_content_type() { private function _check_query_string() { $accept_qs = $this->_config->get_array( 'pgcache.accept.qs' ); + Util_Rule::array_trim( $accept_qs ); + + if ( empty( $accept_qs) ) { + return false; + } + + foreach ( $accept_qs as &$val ) { + $val = Util_Environment::preg_quote( str_replace( "+", " ", $val ) ); + $val .= ( strpos( $val, '=' ) === false ? '=.*?' : '' ); + } + + $accept_qs = implode( '|', $accept_qs ); + foreach ( $_GET as $key => $value ) { - if ( !in_array( strtolower( $key ), $accept_qs ) ) + if ( !@preg_match( '~^(' . $accept_qs . ')$~i', $key . "=$value" ) ) { return false; + } } + return true; } @@ -1644,10 +1810,10 @@ private function _maybe_save_cached_result( $buffer, $has_dynamic ) { return $buffer; } - $mobile_group = $this->_get_mobile_group(); - $referrer_group = $this->_get_referrer_group(); - $encryption = $this->_get_encryption(); - $compression_header = $this->_get_compression(); + $mobile_group = $this->_page_key_extension['useragent']; + $referrer_group = $this->_page_key_extension['referrer']; + $encryption = $this->_page_key_extension['encryption']; + $compression_header = $this->_page_key_extension['compression']; $compressions_to_store = $this->_get_compressions(); /** @@ -1663,7 +1829,6 @@ private function _maybe_save_cached_result( $buffer, $has_dynamic ) { $compression_of_returned_content = ( $has_dynamic ? false : $compression_header ); - $content_type = ''; $is_404 = ( function_exists( 'is_404' ) ? is_404() : false ); $response_headers = $this->_get_response_headers(); $headers = $this->_get_cached_headers( $response_headers['plain'] ); @@ -1678,9 +1843,9 @@ private function _maybe_save_cached_result( $buffer, $has_dynamic ) { // they will be turned into fresh files and catch further requests if ( isset( $response_headers['kv']['Location'] ) ) { foreach ( $compressions_to_store as $_compression ) { - $_page_key = $this->_get_page_key( $mobile_group, - $referrer_group, $encryption, $_compression, - $content_type ); + $_page_key = $this->_get_page_key( + array_merge( $this->_page_key_extension, + array( 'compression' => $_compression ) ) ); $cache = $this->_get_cache(); $cache->hard_delete( $_page_key ); } @@ -1689,6 +1854,7 @@ private function _maybe_save_cached_result( $buffer, $has_dynamic ) { } } + $content_type = ''; if ( $this->_enhanced_mode && !$this->_late_init ) { register_shutdown_function( array( $this, @@ -1718,9 +1884,11 @@ private function _maybe_save_cached_result( $buffer, $has_dynamic ) { $group = 'sitemaps'; foreach ( $compressions_to_store as $_compression ) { - $this->_set_extract_page_key( $mobile_group, - $referrer_group, $encryption, $_compression, - $content_type, true ); + $this->_set_extract_page_key( + array_merge( $this->_page_key_extension, + array( + 'compression' => $_compression, + 'content_type' => $content_type ) ), true ); if ( empty( $this->_page_key ) ) continue; diff --git a/PgCache_Environment.php b/PgCache_Environment.php index 8293a8f..076aedb 100644 --- a/PgCache_Environment.php +++ b/PgCache_Environment.php @@ -582,6 +582,7 @@ private function rules_core_generate_apache( $config ) { */ $env_W3TC_UA = ''; $env_W3TC_REF = ''; + $env_W3TC_COOKIE = ''; $env_W3TC_SSL = ''; $env_W3TC_ENC = ''; @@ -596,6 +597,30 @@ private function rules_core_generate_apache( $config ) { $rules .= " RewriteRule ^(.*\\/)?w3tc_rewrite_test([0-9]+)/?$ $1?w3tc_rewrite_test=1 [L]\n"; } + + /** + * Set accept query strings + */ + $w3tc_query_strings = $config->get_array( 'pgcache.accept.qs' ); + Util_Rule::array_trim( $w3tc_query_strings ); + + if ( !empty( $w3tc_query_strings ) ) { + $w3tc_query_strings = str_replace( ' ', '+', $w3tc_query_strings ); + $w3tc_query_strings = array_map( array( '\W3TC\Util_Environment', 'preg_quote' ), $w3tc_query_strings ); + + $rules .= " RewriteRule ^ - [E=W3TC_QUERY_STRING:%{QUERY_STRING}]\n"; + + foreach ( $w3tc_query_strings as $query ) { + $query .= ( strpos( $query, '=' ) === false ? '=.*?' : '' ); + $rules .= " RewriteCond %{ENV:W3TC_QUERY_STRING} ^(.*?&|)" . + $query . "(&.*|)$ [NC]\n"; + $rules .= " RewriteRule ^ - [E=W3TC_QUERY_STRING:%1%2]\n"; + } + + $rules .= " RewriteCond %{ENV:W3TC_QUERY_STRING} ^&+$\n"; + $rules .= " RewriteRule ^ - [E=W3TC_QUERY_STRING]\n"; + } + /** * Check for mobile redirect */ @@ -670,6 +695,36 @@ private function rules_core_generate_apache( $config ) { } } + /** + * Set cookie group + */ + if ( $config->get_boolean( 'pgcache.cookiegroups.enabled' ) ) { + $cookie_groups = $config->get_array( 'pgcache.cookiegroups.groups' ); + + foreach ( $cookie_groups as $group_name => $g ) { + if ( isset( $g['enabled'] ) && $g['enabled'] ) { + $cookies = array(); + foreach ($g['cookies'] as $cookie ) { + $cookie = trim( $cookie ); + if ( !empty( $cookie ) ) { + $cookie = str_replace( '+', ' ', $cookie ); + $cookie = Util_Environment::preg_quote( $cookie ); + if ( strpos( $cookie, '=') === false ) + $cookie .= '=.*'; + $cookies[] = $cookie; + } + } + + if ( count( $cookies ) > 0 ) { + $cookies_regexp = '^(.*;\s*)?(' . implode( '|', $cookies ) . ')(\s*;.*)?$'; + $rules .= " RewriteCond %{HTTP_COOKIE} $cookies_regexp [NC]\n"; + $rules .= " RewriteRule .* - [E=W3TC_COOKIE:_" . $group_name . "]\n"; + $env_W3TC_COOKIE = '%{ENV:W3TC_COOKIE}'; + } + } + } + } + /** * Set HTTPS */ @@ -678,6 +733,8 @@ private function rules_core_generate_apache( $config ) { $rules .= " RewriteRule .* - [E=W3TC_SSL:_ssl]\n"; $rules .= " RewriteCond %{SERVER_PORT} =443\n"; $rules .= " RewriteRule .* - [E=W3TC_SSL:_ssl]\n"; + $rules .= " RewriteCond %{HTTP:X-Forwarded-Proto} =https [NC]\n"; + $rules .= " RewriteRule .* - [E=W3TC_SSL:_ssl]\n"; $env_W3TC_SSL = '%{ENV:W3TC_SSL}'; } @@ -704,7 +761,9 @@ private function rules_core_generate_apache( $config ) { /** * Query string should be empty */ - $use_cache_rules .= " RewriteCond %{QUERY_STRING} =\"\"\n"; + $use_cache_rules .= empty( $w3tc_query_strings ) ? + " RewriteCond %{QUERY_STRING} =\"\"\n" : + " RewriteCond %{ENV:W3TC_QUERY_STRING} =\"\"\n"; /** * Check permalink structure trailing slash @@ -732,18 +791,24 @@ private function rules_core_generate_apache( $config ) { * Make final rewrites for specific files */ $uri_prefix = $cache_path . '/%{HTTP_HOST}/%{REQUEST_URI}/' . - '_index' . $env_W3TC_UA . $env_W3TC_REF . $env_W3TC_SSL . $env_W3TC_PREVIEW; + '_index' . $env_W3TC_UA . $env_W3TC_REF . $env_W3TC_COOKIE . + $env_W3TC_SSL . $env_W3TC_PREVIEW; $switch = " -" . ( $config->get_boolean( 'pgcache.file.nfs' ) ? 'F' : 'f' ); $document_root = Util_Rule::apache_docroot_variable(); - // write rule to rewrite to .html file - $ext = '.html'; - $rules .= $use_cache_rules; - $rules .= " RewriteCond \"" . $document_root . $uri_prefix . $ext . - $env_W3TC_ENC . "\"" . $switch . "\n"; - $rules .= " RewriteRule .* \"" . $uri_prefix . $ext . - $env_W3TC_ENC . "\" [L]\n"; + // write rule to rewrite to .html/.xml file + $exts = array( '.html' ); + if ($config->get_boolean('pgcache.cache.nginx_handle_xml')) + $exts[] = '.xml'; + + foreach ( $exts as $ext ) { + $rules .= $use_cache_rules; + $rules .= " RewriteCond \"" . $document_root . $uri_prefix . $ext . + $env_W3TC_ENC . "\"" . $switch . "\n"; + $rules .= " RewriteRule .* \"" . $uri_prefix . $ext . + $env_W3TC_ENC . "\" [L]\n"; + } $rules .= "\n"; @@ -812,6 +877,7 @@ private function rules_core_generate_nginx( $config ) { */ $env_w3tc_ua = ''; $env_w3tc_ref = ''; + $env_w3tc_cookie = ''; $env_w3tc_ssl = ''; $env_w3tc_ext = ''; $env_w3tc_enc = ''; @@ -822,6 +888,30 @@ private function rules_core_generate_nginx( $config ) { $rules .= "rewrite ^(.*\\/)?w3tc_rewrite_test([0-9]+)/?$ $1?w3tc_rewrite_test=1 last;\n"; } + /** + * Set accept query strings + */ + $w3tc_query_strings = $config->get_array( 'pgcache.accept.qs' ); + Util_Rule::array_trim( $w3tc_query_strings ); + + if ( !empty( $w3tc_query_strings ) ) { + $w3tc_query_strings = str_replace( ' ', '+', $w3tc_query_strings ); + $w3tc_query_strings = array_map( array( '\W3TC\Util_Environment', 'preg_quote' ), $w3tc_query_strings ); + + $rules .= "set \$w3tc_query_string \$query_string;\n"; + + foreach ( $w3tc_query_strings as $query ) { + $query .= ( strpos( $query, '=' ) === false ? '.*?' : '' ); + $rules .= "if (\$w3tc_query_string ~* \"^(.*?&|)".$query."(&.*|)$\") {\n"; + $rules .= " set \$w3tc_query_string $1$2;\n"; + $rules .= "}\n"; + } + + $rules .= "if (\$w3tc_query_string ~ ^&+$) {\n"; + $rules .= " set \$w3tc_query_string \"\";\n"; + $rules .= "}\n"; + } + /** * Check for mobile redirect */ @@ -880,7 +970,10 @@ private function rules_core_generate_nginx( $config ) { /** * Query string should be empty */ - $rules .= "if (\$query_string != \"\") {\n"; + $querystring_variable = ( empty( $w3tc_query_strings ) ? + '$query_string' : '$w3tc_query_string' ); + + $rules .= "if (" . $querystring_variable . " != \"\") {\n"; $rules .= " set \$w3tc_rewrite 0;\n"; $rules .= "}\n"; @@ -981,12 +1074,53 @@ private function rules_core_generate_nginx( $config ) { } } + /** + * Set cookie group + */ + if ( $config->get_boolean( 'pgcache.cookiegroups.enabled' ) ) { + $cookie_groups = $config->get_array( 'pgcache.cookiegroups.groups' ); + $set_cookie_var = true; + + foreach ( $cookie_groups as $group_name => $g ) { + if ( isset( $g['enabled'] ) && $g['enabled'] ) { + $cookies = array(); + foreach ($g['cookies'] as $cookie ) { + $cookie = trim( $cookie ); + if ( !empty( $cookie ) ) { + $cookie = str_replace( '+', ' ', $cookie ); + $cookie = Util_Environment::preg_quote( $cookie ); + if ( strpos( $cookie, '=') === false ) + $cookie .= '=.*'; + $cookies[] = $cookie; + } + } + + if ( count( $cookies ) > 0 ) { + $cookies_regexp = '"^(.*;)?(' . implode( '|', $cookies ) . ')(;.*)?$"'; + + if ( $set_cookie_var ) { + $rules .= "set \$w3tc_cookie \"\";\n"; + $set_cookie_var = false; + } + $rules .= "if (\$http_cookie ~* $cookies_regexp) {\n"; + $rules .= " set \$w3tc_cookie _" . $group_name . ";\n"; + $rules .= "}\n"; + + $env_w3tc_cookie = "\$w3tc_cookie"; + } + } + } + } + if ( $config->get_boolean( 'pgcache.cache.ssl' ) ) { $rules .= "set \$w3tc_ssl \"\";\n"; $rules .= "if (\$scheme = https) {\n"; $rules .= " set \$w3tc_ssl _ssl;\n"; $rules .= "}\n"; + $rules .= "if (\$http_x_forwarded_proto = 'https') {\n"; + $rules .= " set \$w3tc_ssl _ssl;\n"; + $rules .= "}\n"; $env_w3tc_ssl = "\$w3tc_ssl"; } @@ -1005,7 +1139,7 @@ private function rules_core_generate_nginx( $config ) { $cache_path = str_replace( Util_Environment::document_root(), '', $cache_dir ); $uri_prefix = $cache_path . "/\$http_host/" . "\$request_uri/_index" . $env_w3tc_ua . $env_w3tc_ref . - $env_w3tc_ssl . $env_w3tc_preview; + $env_w3tc_cookie . $env_w3tc_ssl . $env_w3tc_preview; if ( !$config->get_boolean( 'pgcache.cache.nginx_handle_xml' ) ) { $env_w3tc_ext = '.html'; @@ -1134,7 +1268,14 @@ private function rules_cache_generate_apache( $config ) { // allow to read files by apache if they are blocked at some level above $rules .= "\n"; - $rules .= " Allow from all\n"; + + if ( version_compare( Util_Environment::get_server_version(), '2.4', '>=' ) ) { + $rules .= " Require all granted\n"; + } else { + $rules .= " Order Allow,Deny\n"; + $rules .= " Allow from all\n"; + } + $rules .= "\n"; if ( !$etag ) { @@ -1326,14 +1467,23 @@ private function rules_cache_generate_nginx( $config ) { $rules = ''; $rules .= W3TC_MARKER_BEGIN_PGCACHE_CACHE . "\n"; - $rules .= "location ~ " . $cache_dir . ".*html$ {\n"; - $rules .= $common_rules; - $rules .= "}\n"; + if ( !empty( $common_rules ) ) { + $rules .= "location ~ " . $cache_dir . ".*html$ {\n"; + $rules .= $common_rules; + $rules .= "}\n"; + } if ( $compression ) { + $maybe_xml = ''; + if ($config->get_boolean('pgcache.cache.nginx_handle_xml')) { + $maybe_xml = "\n" . + " text/xml xml_gzip\n" . + " "; + } + $rules .= "location ~ " . $cache_dir . ".*gzip$ {\n"; $rules .= " gzip off;\n"; - $rules .= " types {}\n"; + $rules .= " types {" . $maybe_xml . "}\n"; $rules .= " default_type text/html;\n"; $rules .= $common_rules; $rules .= " add_header Content-Encoding gzip;\n"; diff --git a/PgCache_Flush.php b/PgCache_Flush.php index f3dbd94..9c8a587 100644 --- a/PgCache_Flush.php +++ b/PgCache_Flush.php @@ -45,7 +45,7 @@ function flush_post( $post_id = null ) { return false; $full_urls = array(); - $post = null; + $post = get_post( $post_id ); $terms = array(); $feeds = $this->_config->get_array( 'pgcache.purge.feed.types' ); @@ -58,9 +58,6 @@ function flush_post( $post_id = null ) { $terms = $this->_append_parent_terms( $terms, $terms ); } - $post = get_post( $post_id ); - $post_type = in_array( $post->post_type, array( - 'post', 'page', 'attachment', 'revision' ) ) ? null : $post->post_type; $front_page = get_option( 'show_on_front' ); /** @@ -102,7 +99,7 @@ function_exists( 'get_comments_pagenum_link' ) ) { /** * Post author URLs */ - if ( $this->_config->get_boolean( 'pgcache.purge.author' ) ) { + if ( $this->_config->get_boolean( 'pgcache.purge.author' ) && $post ) { $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_author_urls( $post->post_author, $limit_post_pages ) ); @@ -143,7 +140,13 @@ function_exists( 'get_comments_pagenum_link' ) ) { /** * Feed URLs */ - if ( $this->_config->get_boolean( 'pgcache.purge.feed.blog' ) ) { + if ( $this->_config->get_boolean( 'pgcache.purge.feed.blog' ) && $post ) { + $post_type = null; + if ( in_array( $post->post_type, + array( 'post', 'page', 'attachment', 'revision' ) ) ) { + $post_type = $post->post_type; + } + $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_urls( $feeds, $post_type ) ); } @@ -205,8 +208,11 @@ function _flush_url( $url, $cache, $mobile_groups, $referrer_groups, foreach ( $encryptions as $encryption ) { foreach ( $compressions as $compression ) { $page_keys = array(); - $page_keys[] = $this->_get_page_key( $mobile_group, - $referrer_group, $encryption, $compression, false, + $page_keys[] = $this->_get_page_key( array( + 'useragent' => $mobile_group, + 'referrer' => $referrer_group, + 'encryption' => $encryption, + 'compression' => $compression ), $url ); $page_keys = apply_filters( 'w3tc_pagecache_flush_url_keys', $page_keys ); diff --git a/PgCache_Page.php b/PgCache_Page.php index 31531a6..caf9947 100644 --- a/PgCache_Page.php +++ b/PgCache_Page.php @@ -33,9 +33,7 @@ function view() { $permalink_structure = get_option( 'permalink_structure' ); $varnish_enabled = $this->_config->get_boolean( 'varnish.enabled' ); - $cdn_mirror_purge_enabled = - Cdn_Util::is_engine_fsd( $this->_config->get_string( 'cdn.engine' ) ) && - $this->_config->get_boolean( 'cdn.enabled' ); + $cdnfsd_enabled = $this->_config->get_boolean( 'cdnfsd.enabled' ); include W3TC_INC_DIR . '/options/pgcache.php'; } } diff --git a/PgCache_Page_CookieGroups.php b/PgCache_Page_CookieGroups.php new file mode 100644 index 0000000..ffde196 --- /dev/null +++ b/PgCache_Page_CookieGroups.php @@ -0,0 +1,78 @@ + $c->get_array( 'pgcache.cookiegroups.groups' ), + 'disabled' => $c->is_sealed( 'pgcache.cookiegroups.groups' ) + ); + + $groups = apply_filters( 'w3tc_ui_config_item_pgcache.cookiegroups.groups', $groups ); + $config = Dispatcher::config(); + + include W3TC_DIR . '/PgCache_Page_CookieGroups_View.php'; + } + + + + static public function w3tc_config_ui_save_w3tc_pgcache_cookiegroups( $config ) { + $groups = Util_Request::get_array( 'cookiegroups' ); + + $mobile_groups = array(); + $cached_mobile_groups = array(); + + foreach ( $groups as $group => $group_config ) { + $group = strtolower( $group ); + $group = preg_replace( '~[^0-9a-z_]+~', '_', $group ); + $group = trim( $group, '_' ); + + if ( $group ) { + $enabled = ( isset( $group_config['enabled'] ) ? + (boolean) $group_config['enabled'] : false ); + $cache = ( isset( $group_config['cache'] ) ? + (boolean) $group_config['cache'] : false ); + $cookies = ( isset( $group_config['cookies'] ) ? + explode( "\r\n", trim( $group_config['cookies'] ) ) : + array() ); + + $cookies = array_unique( $cookies ); + sort( $cookies ); + + $cookiegroups[$group] = array( + 'enabled' => $enabled, + 'cache' => $cache, + 'cookies' => $cookies + ); + } + } + + /** + * Allow plugins modify W3TC mobile groups + */ + $cookiegroups = apply_filters( 'w3tc_pgcache_cookiegroups', $cookiegroups ); + + $enabled = false; + foreach ( $cookiegroups as $group_config ) { + if ( $group_config['enabled'] ) { + $enabled = true; + break; + } + } + $config->set( 'pgcache.cookiegroups.enabled', $enabled ); + $config->set( 'pgcache.cookiegroups.groups', $cookiegroups ); + } +} diff --git a/PgCache_Page_CookieGroups_View.js b/PgCache_Page_CookieGroups_View.js new file mode 100644 index 0000000..6008441 --- /dev/null +++ b/PgCache_Page_CookieGroups_View.js @@ -0,0 +1,96 @@ +jQuery(function() { + + +function w3tc_cookiegroups_clear() { + if (!jQuery('#cookiegroups li').size()) { + jQuery('#cookiegroups_empty').show(); + } else { + jQuery('#cookiegroups_empty').hide(); + } +} + + + +jQuery('#w3tc_cookiegroup_add').click(function() { + var group = prompt('Enter group name (only "0-9", "a-z", "_" symbols are allowed).'); + + if (group !== null) { + group = group.toLowerCase(); + group = group.replace(/[^0-9a-z_]+/g, '_'); + group = group.replace(/^_+/, ''); + group = group.replace(/_+$/, ''); + + if (group) { + var exists = false; + + jQuery('.cookiegroup_name').each(function() { + if (jQuery(this).html() == group) { + alert('Group already exists!'); + exists = true; + return false; + } + }); + + if (!exists) { + var li = jQuery('
  • ' + + '
  • #StatusSourceData size (b)Query time (s)ID:Group
    #OperationReturnedData size (b)Query time (s)ID:Group
    ' . ( $index + 1 ) . '' . ( $debug['cached'] ? 'cached' : 'not cached' ) . '' . ( $debug['internal'] ? 'internal' : 'persistent' ) . '' . $debug['operation'] . '' . $debug['returned'] . '' . $debug['data_size'] . '' . round( $debug['time'], 4 ) . '' . sprintf( '%s:%s', $debug['id'], $debug['group'] ) . '
    ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    Group name:' + (jQuery('#cookiegroups li').size() + 1) + '. ' + + '' + group + ' ' + + '
    ' + + '
    ' + + '

    ' + + 'Specify the cookies for this group. Values like \'cookie\', \'cookie=value\', and cookie[a-z]+=value[a-z]+are supported. Remember to escape special characters like spaces, dots or dashes with a backslash. Regular expressions are also supported.
    '); + var select = li.find('select'); + + jQuery('#cookiegroups').append(li); + w3tc_cookiegroups_clear(); + window.location.hash = '#cookiegroup_' + group; + li.find('textarea').focus(); + } + } else { + alert('Empty group name!'); + } + } +}); + +jQuery('.w3tc_cookiegroup_delete').live('click', function() { + if (confirm('Are you sure want to delete this group?')) { + jQuery(this).parents('#cookiegroups li').remove(); + w3tc_cookiegroups_clear(); + w3tc_beforeupload_bind(); + } +}); + +w3tc_cookiegroups_clear(); + +// add sortable +if (jQuery.ui && jQuery.ui.sortable) { + jQuery('#cookiegroups').sortable({ + axis: 'y', + stop: function() { + jQuery('#cookiegroups').find('.cookiegroup_number').each(function(index) { + jQuery(this).html((index + 1) + '.'); + }); + } + }); +} + + +}); diff --git a/PgCache_Page_CookieGroups_View.php b/PgCache_Page_CookieGroups_View.php new file mode 100644 index 0000000..0db8edd --- /dev/null +++ b/PgCache_Page_CookieGroups_View.php @@ -0,0 +1,113 @@ + + +

    + enabled.', 'w3-total-cache' ); ?> +

    + +
    +
    + +

    + + value="" /> + +

    + +
      + $group_config ): $index++; ?> +
    • + + + + + + + + + + + + + + + + + +
      + + + . + + /> +
      + + + value="1" + /> +
      + + + value="1" + /> +
      + + + +
      + + + +
      +
    • + +
    + + + + + + + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/PgCache_Plugin_Admin.php b/PgCache_Plugin_Admin.php index f56b42b..586b365 100644 --- a/PgCache_Plugin_Admin.php +++ b/PgCache_Plugin_Admin.php @@ -35,6 +35,24 @@ function run() { add_filter( 'w3tc_usage_statistics_summary_from_history', array( $this, 'w3tc_usage_statistics_summary_from_history' ), 10, 2 ); } + + // cookie groups + add_filter( 'w3tc_admin_menu', array( $this, 'w3tc_admin_menu' ) ); + add_action( 'admin_init_w3tc_pgcache_cookiegroups', array( + '\W3TC\PgCache_Page_CookieGroups', + 'admin_init_w3tc_pgcache_cookiegroups' + ) ); + + add_action( 'w3tc_settings_page-w3tc_pgcache_cookiegroups', array( + '\W3TC\PgCache_Page_CookieGroups', + 'w3tc_settings_page_w3tc_pgcache_cookiegroups' + ) ); + + add_action( 'w3tc_config_ui_save-w3tc_pgcache_cookiegroups', array( + '\W3TC\PgCache_Page_CookieGroups', + 'w3tc_config_ui_save_w3tc_pgcache_cookiegroups' + ), 10, 1 ); + } function cleanup() { @@ -206,6 +224,20 @@ function parse_sitemap( $url ) { arsort( $locs ); $urls = array_keys( $locs ); + } elseif ( preg_match_all( '~]*>(.*?)~is', $response['body'], $sitemap_matches ) ) { + + // rss feed format + if ( preg_match_all( '~]*>(.*?)~is', $response['body'], $url_matches ) ) { + foreach ( $url_matches[1] as $url_match ) { + $url = trim( $url_match ); + $cdata_matches = null; + if ( preg_match( '~~is', $url, $cdata_matches ) ) { + $url = $cdata_matches[1]; + } + + $urls[] = $url; + } + } } } @@ -268,6 +300,17 @@ public function w3tc_errors( $errors ) { return $errors; } + public function w3tc_admin_menu( $menu ) { + $menu['w3tc_pgcache_cookiegroups'] = array( + 'page_title' => __( 'Cookie Groups', 'w3-total-cache' ), + 'menu_text' => __( 'Cookie Groups', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 950 + ); + + return $menu; + } + public function w3tc_usage_statistics_summary_from_history( $summary, $history ) { // memcached servers if ( $this->_config->get_string( 'pgcache.engine' ) == 'memcached' ) { diff --git a/Root_AdminActions.php b/Root_AdminActions.php index 8e8940f..8cfa5e7 100644 --- a/Root_AdminActions.php +++ b/Root_AdminActions.php @@ -45,7 +45,6 @@ private function _get_handler( $action ) { 'config' => 'Generic_AdminActions_Config', 'test' => 'Generic_AdminActions_Test', 'licensing' => 'Licensing_AdminActions', - 'edge_mode' => 'Generic_AdminActions_EdgeMode', 'extensions' => 'Extensions_AdminActions', 'default' => 'Generic_AdminActions_Default', 'support' => 'Support_AdminActions' diff --git a/Root_AdminActivation.php b/Root_AdminActivation.php index a6a0373..1616d84 100644 --- a/Root_AdminActivation.php +++ b/Root_AdminActivation.php @@ -43,7 +43,7 @@ static public function activate( $network_wide ) { // try to save config file if needed, optional thing so exceptions // hidden - if ( !file_exists( Config::util_config_filename( 0, false ) ) ) { + if ( !ConfigUtil::is_item_exists( 0, false ) ) { try { // create folders $e->fix_in_wpadmin( $config ); diff --git a/Root_AdminMenu.php b/Root_AdminMenu.php index b6156b7..8616a3a 100644 --- a/Root_AdminMenu.php +++ b/Root_AdminMenu.php @@ -25,86 +25,101 @@ function generate_menu_array() { 'w3tc_dashboard' => array( 'page_title' => __( 'Dashboard', 'w3-total-cache' ), 'menu_text' => __( 'Dashboard', 'w3-total-cache' ), - 'visible_always' => true + 'visible_always' => true, + 'order' => 100 ), 'w3tc_general' => array( 'page_title' => __( 'General Settings', 'w3-total-cache' ), 'menu_text' => __( 'General Settings', 'w3-total-cache' ), - 'visible_always' => false + 'visible_always' => false, + 'order' => 200 ), 'w3tc_pgcache' => array( 'page_title' => __( 'Page Cache', 'w3-total-cache' ), 'menu_text' => __( 'Page Cache', 'w3-total-cache' ), - 'visible_always' => false + 'visible_always' => false, + 'order' => 300 ), 'w3tc_minify' => array( 'page_title' => __( 'Minify', 'w3-total-cache' ), 'menu_text' => __( 'Minify', 'w3-total-cache' ), - 'visible_always' => false + 'visible_always' => false, + 'order' => 400 ), 'w3tc_dbcache' => array( 'page_title' => __( 'Database Cache', 'w3-total-cache' ), 'menu_text' => __( 'Database Cache', 'w3-total-cache' ), - 'visible_always' => false + 'visible_always' => false, + 'order' => 500 ), 'w3tc_objectcache' => array( 'page_title' => __( 'Object Cache', 'w3-total-cache' ), 'menu_text' => __( 'Object Cache', 'w3-total-cache' ), - 'visible_always' => false - ) - ); - $pages = array_merge( $pages, array( - 'w3tc_browsercache' => array( - 'page_title' => __( 'Browser Cache', 'w3-total-cache' ), - 'menu_text' => __( 'Browser Cache', 'w3-total-cache' ), - 'visible_always' => false - ), - 'w3tc_mobile' => array( - 'page_title' => __( 'User Agent Groups', 'w3-total-cache' ), - 'menu_text' => __( 'User Agent Groups', 'w3-total-cache' ), - 'visible_always' => false - ), - 'w3tc_referrer' => array( - 'page_title' => __( 'Referrer Groups', 'w3-total-cache' ), - 'menu_text' => __( 'Referrer Groups', 'w3-total-cache' ), - 'visible_always' => false - ), - 'w3tc_cdn' => array( - 'page_title' => __( 'Content Delivery Network', 'w3-total-cache' ), - 'menu_text' => __( 'CDN', 'w3-total-cache' ), - 'visible_always' => false - ) - ) ); - $pages_tail = array( + 'visible_always' => false, + 'order' => 600 + ), + 'w3tc_browsercache' => array( + 'page_title' => __( 'Browser Cache', 'w3-total-cache' ), + 'menu_text' => __( 'Browser Cache', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 700 + ), + 'w3tc_mobile' => array( + 'page_title' => __( 'User Agent Groups', 'w3-total-cache' ), + 'menu_text' => __( 'User Agent Groups', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 800 + ), + 'w3tc_referrer' => array( + 'page_title' => __( 'Referrer Groups', 'w3-total-cache' ), + 'menu_text' => __( 'Referrer Groups', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 900 + ), + 'w3tc_cdn' => array( + 'page_title' => __( 'Content Delivery Network', 'w3-total-cache' ), + 'menu_text' => __( 'CDN', 'w3-total-cache' ), + 'visible_always' => false, + 'order' => 1000 + ), 'w3tc_faq' => array( 'page_title' => __( 'FAQ', 'w3-total-cache' ), 'menu_text' => __( 'FAQ', 'w3-total-cache' ), - 'visible_always' => true + 'visible_always' => true, + 'order' => 2000 ), 'w3tc_support' => array( 'page_title' => __( 'Support', 'w3-total-cache' ), 'menu_text' => __( 'Support', 'w3-total-cache' ), - 'visible_always' => true + 'visible_always' => true, + 'order' => 2100 ), 'w3tc_install' => array( 'page_title' => __( 'Install', 'w3-total-cache' ), 'menu_text' => __( 'Install', 'w3-total-cache' ), - 'visible_always' => false + 'visible_always' => false, + 'order' => 2200 ), 'w3tc_about' => array( 'page_title' => __( 'About', 'w3-total-cache' ), 'menu_text' => __( 'About', 'w3-total-cache' ), - 'visible_always' => true + 'visible_always' => true, + 'order' => 2300 ) ); $pages = apply_filters( 'w3tc_admin_menu', $pages, $this->_config ); - $pages = array_merge( $pages, $pages_tail ); + return $pages; } function generate( $base_capability ) { $pages = $this->generate_menu_array(); + uasort( $pages, function($a, $b) { + return ($a['order'] - $b['order']); + } + ); + add_menu_page( __( 'Performance', 'w3-total-cache' ), __( 'Performance', 'w3-total-cache' ), apply_filters( 'w3tc_capability_menu_w3tc_dashboard', diff --git a/Root_Loader.php b/Root_Loader.php index a563540..a9b8fdb 100644 --- a/Root_Loader.php +++ b/Root_Loader.php @@ -29,6 +29,8 @@ function __construct() { $plugins[] = new PgCache_Plugin(); if ( $c->get_boolean( 'cdn.enabled' ) ) $plugins[] = new Cdn_Plugin(); + if ( $c->get_boolean( 'cdnfsd.enabled' ) ) + $plugins[] = new Cdnfsd_Plugin(); if ( $c->get_boolean( 'browsercache.enabled' ) ) $plugins[] = new BrowserCache_Plugin(); if ( $c->get_boolean( 'minify.enabled' ) ) @@ -51,18 +53,18 @@ function __construct() { $plugins[] = new SystemOpCache_Plugin_Admin(); $plugins[] = new Cdn_Plugin_Admin(); + $plugins[] = new Cdnfsd_Plugin_Admin(); if ( $c->get_string( 'cdn.engine' ) == 'highwinds' ) { - } else if ( $c->get_string( 'cdn.engine' ) != 'netdna' ) - $plugins[] = new Cdn_Plugin_WidgetMaxCdn(); - else - $plugins[] = new Cdn_Plugin_WidgetNetDna(); + } else { + $plugins[] = new Cdn_Plugin_WidgetMaxCdn(); + } - if ( $c->get_boolean( 'widget.pagespeed.enabled' ) ) - $plugins[] = new PageSpeed_Plugin_Widget(); + if ( $c->get_boolean( 'widget.pagespeed.enabled' ) ) + $plugins[] = new PageSpeed_Plugin_Widget(); - $plugins[] = new Generic_Plugin_AdminCompatibility(); + $plugins[] = new Generic_Plugin_AdminCompatibility(); - if ( !( defined( 'W3TC_PRO' ) || Util_Environment::is_w3tc_enterprise() ) ) + if ( !( defined( 'W3TC_PRO' ) || defined( 'W3TC_ENTERPRISE' ) ) ) $plugins[] = new Licensing_Plugin_Admin(); if ( $c->get_boolean( 'pgcache.enabled' ) || diff --git a/Support_AdminActions.php b/Support_AdminActions.php index 36e9676..2bf097a 100644 --- a/Support_AdminActions.php +++ b/Support_AdminActions.php @@ -21,8 +21,6 @@ function w3tc_support_send_details() { $license_level = 'community'; if ( Util_Environment::is_w3tc_pro( $c ) ) $license_level = 'pro'; - elseif ( Util_Environment::is_w3tc_enterprise( $c ) ) - $license_level = 'enterprise'; $post['license_level'] = $license_level . ' ' . $c->get_string( 'plugin.license_key' ); diff --git a/Util_Admin.php b/Util_Admin.php index de31962..c55edd0 100644 --- a/Util_Admin.php +++ b/Util_Admin.php @@ -313,8 +313,6 @@ static public function config_save( $current_config, $new_config ) { 'cdn.azure.ssl', 'cdn.mirror.domain', 'cdn.mirror.ssl', - 'cdn.netdna.domain', - 'cdn.netdna.ssl', 'cdn.cotendo.domain', 'cdn.cotendo.ssl', 'cdn.edgecast.domain', @@ -742,30 +740,10 @@ static public function get_cookie_domain() { static public function get_current_page() { $page = Util_Request::get_string( 'page' ); - switch ( true ) { - case ( $page == 'w3tc_dashboard' ): - case ( $page == 'w3tc_general' ): - case ( $page == 'w3tc_pgcache' ): - case ( $page == 'w3tc_minify' ): - case ( $page == 'w3tc_dbcache' ): - case ( $page == 'w3tc_objectcache' ): - case ( $page == 'w3tc_fragmentcache' ): - case ( $page == 'w3tc_browsercache' ): - case ( $page == 'w3tc_mobile' ): - case ( $page == 'w3tc_referrer' ): - case ( $page == 'w3tc_cdn' ): - case ( $page == 'w3tc_extensions' ): - case ( $page == 'w3tc_install' ): - case ( $page == 'w3tc_faq' ): - case ( $page == 'w3tc_about' ): - case ( $page == 'w3tc_support' ): - break; - - default: - $page = 'w3tc_dashboard'; - } + if ( substr( $page, 0, 5 ) == 'w3tc_' ) + return $page; - return $page; + return 'w3tc_dashboard'; } /** diff --git a/Util_AttachToActions.php b/Util_AttachToActions.php index b8e2be8..8a4bf4f 100644 --- a/Util_AttachToActions.php +++ b/Util_AttachToActions.php @@ -19,6 +19,18 @@ static function flush_posts_on_actions() { $o, 'on_post_change' ), 0, 2 ); + + // when post status is changed to draft - it looses its URL + // so we need to flush before update is happened + add_action( 'pre_post_update', array( + $o, + 'on_post_change' + ), 0 ); + add_action( 'wp_trash_post', array( + $o, + 'on_post_change' + ), 0 ); + add_action( 'publish_post', array( $o, 'on_post_change' diff --git a/Util_Debug.php b/Util_Debug.php index 45c48c2..8be5af0 100644 --- a/Util_Debug.php +++ b/Util_Debug.php @@ -50,4 +50,13 @@ static public function log_filename( $module, $blog_id = null ) { return $filename; } + + + + static public function log( $module, $message ) { + $message = strtr( $message, '<>', '..' ); + $filename = Util_Debug::log_filename( $module ); + + return @file_put_contents( $filename, date( 'r' ) . ' ' . $message . "\n", FILE_APPEND ); + } } diff --git a/Util_Environment.php b/Util_Environment.php index 3c0c910..083593c 100644 --- a/Util_Environment.php +++ b/Util_Environment.php @@ -135,9 +135,14 @@ static public function filename_to_url( $filename, $use_site_url = false ) { * @return boolean */ static public function is_dbcluster() { + if ( !defined( 'W3TC_PRO' ) || !W3TC_PRO ) + return false; + + if ( isset( $GLOBALS['w3tc_dbcluster_config'] ) ) + return true; + return defined( 'W3TC_FILE_DB_CLUSTER_CONFIG' ) && - @file_exists( W3TC_FILE_DB_CLUSTER_CONFIG ) - && defined( 'W3TC_ENTERPRISE' ) && W3TC_ENTERPRISE; + @file_exists( W3TC_FILE_DB_CLUSTER_CONFIG ); } /** @@ -844,7 +849,7 @@ static public function translate_file( $file ) { * Removes WP query string from URL */ static public function remove_query( $url ) { - $url = preg_replace( '~[&\?]+(ver=([a-z0-9-_\.]+|[0-9-]+))~i', '', $url ); + $url = preg_replace( '~(\?|&|&|&)+ver=[a-z0-9-_\.]+~i', '', $url ); return $url; } @@ -1023,6 +1028,10 @@ static public function detect_post_id() { } static public function instance_id() { + if ( defined( 'W3TC_INSTANCE_ID' ) ) { + return W3TC_INSTANCE_ID; + } + static $instance_id; if ( !isset( $instance_id ) ) { @@ -1039,8 +1048,6 @@ static public function instance_id() { * @return string */ static public function w3tc_edition( $config = null ) { - if ( Util_Environment::is_w3tc_enterprise( $config ) ) - return 'enterprise'; if ( Util_Environment::is_w3tc_pro( $config ) && Util_Environment::is_w3tc_pro_dev() ) return 'pro development'; if ( Util_Environment::is_w3tc_pro( $config ) ) @@ -1057,6 +1064,8 @@ static public function w3tc_edition( $config = null ) { static public function is_w3tc_pro( $config = null ) { if ( defined( 'W3TC_PRO' ) && W3TC_PRO ) return true; + if ( defined( 'W3TC_ENTERPRISE' ) && W3TC_ENTERPRISE ) + return true; if ( is_object( $config ) ) { $plugin_type = $config->get_string( 'plugin.type' ); @@ -1065,9 +1074,6 @@ static public function is_w3tc_pro( $config = null ) { return true; } - if ( Util_Environment::is_w3tc_enterprise( $config ) ) - return true; - return false; } @@ -1080,32 +1086,6 @@ static public function is_w3tc_pro_dev() { return defined( 'W3TC_PRO_DEV_MODE' ) && W3TC_PRO_DEV_MODE; } - /** - * - * - * @param Config $config - * @return bool - */ - static public function is_w3tc_enterprise( $config = null ) { - if ( defined( 'W3TC_ENTERPRISE' ) && W3TC_ENTERPRISE ) - return true; - - if ( is_object( $config ) && - $config->get_string( 'plugin.type' ) == 'enterprise' ) - return true; - - return false; - } - - /** - * Checks if site is using edge mode. - * - * @return bool - */ - static public function is_w3tc_edge( $config ) { - return $config->get_boolean( 'common.edge' ); - } - /** * Quotes regular expression string * @@ -1215,4 +1195,16 @@ static public function to_boolean( $value ) { return (boolean) $value; } + + /** + * Returns the apache, nginx version + * + * @return string + */ + static public function get_server_version() { + $sig= explode( '/', $_SERVER['SERVER_SOFTWARE'] ); + $temp = isset( $sig[1] ) ? explode( ' ', $sig[1] ) : array( '0' ); + $version = $temp[0]; + return $version; + } } diff --git a/Util_PageUrls.php b/Util_PageUrls.php index 9ff8103..e5b5726 100644 --- a/Util_PageUrls.php +++ b/Util_PageUrls.php @@ -500,7 +500,9 @@ static public function get_pagenum_link_admin( $pagenum ) { $base = trailingslashit( get_bloginfo( 'url' ) ); - if ( $wp_rewrite->using_index_permalinks() && ( $pagenum > 1 || '' != $request ) ) + if ( !is_null( $wp_rewrite ) && + $wp_rewrite->using_index_permalinks() && + ( $pagenum > 1 || '' != $request ) ) $base .= 'index.php/'; if ( $pagenum > 1 ) { diff --git a/Util_Rule.php b/Util_Rule.php index c0399ad..f089ff0 100644 --- a/Util_Rule.php +++ b/Util_Rule.php @@ -240,9 +240,15 @@ static public function add_rules( $exs, $path, $rules, $start, $end, $order ) { if ( $data === false ) $data = ''; - $rules_missing = !empty( $rules ) && ( strstr( Util_Rule::clean_rules( $data ), Util_Rule::clean_rules( $rules ) ) === false ); - if ( !$rules_missing ) - return; + if ( empty( $rules ) ) { + $rules_present = ( strpos( $data, $start ) !== false ); + if ( !$rules_present ) + return; + } else { + $rules_missing = ( strstr( Util_Rule::clean_rules( $data ), Util_Rule::clean_rules( $rules ) ) === false ); + if ( !$rules_missing ) + return; + } $replace_start = strpos( $data, $start ); $replace_end = strpos( $data, $end ); diff --git a/Util_RuleSnippet.php b/Util_RuleSnippet.php index caed390..8cbbf94 100644 --- a/Util_RuleSnippet.php +++ b/Util_RuleSnippet.php @@ -8,7 +8,8 @@ class Util_RuleSnippet { * @param bool $cdnftp * @return string */ - static public function canonical_without_location( $cdnftp = false, $add_header_rules ) { + static public function canonical_without_location( $cdnftp = false, + $add_header_rules, $cors_header ) { $rules = ''; switch ( true ) { @@ -34,14 +35,17 @@ static public function canonical_without_location( $cdnftp = false, $add_header_ $link_header = ' add_header Link "<$scheme://' . $home . '$uri>; rel=\"canonical\"";' . "\n"; - $rules .= - $link_header . + $rules .= $link_header; + + if ( $cors_header ) { + $rules .= ' if ($request_uri ~ ^[^?]*\\.(ttf|ttc|otf|eot|woff|woff2|font.css)(\\?|$)) {' . "\n " . $link_header . " " . str_replace( "\n", "\n ", $add_header_rules ) . " add_header Access-Control-Allow-Origin \"*\";\n" . " }\n"; + } break; } @@ -55,7 +59,7 @@ static public function canonical_without_location( $cdnftp = false, $add_header_ * @param bool $cdnftp * @return string */ - static public function canonical( $cdnftp = false ) { + static public function canonical( $cdnftp = false, $cors_header ) { $rules = ''; $mime_types = self::_get_other_types(); @@ -68,13 +72,13 @@ static public function canonical( $cdnftp = false ) { $extensions_uppercase = array_map( 'strtoupper', $extensions ); $rules .= "\n"; - $rules .= self::canonical_without_location( $cdnftp, '' ); + $rules .= self::canonical_without_location( $cdnftp, '', $cors_header ); $rules .= "\n"; break; case Util_Environment::is_nginx(): $rules .= "location ~ \.(" . implode( '|', $extensions ) . ")$ {\n"; - $rules .= self::canonical_without_location( $cdnftp, '' ); + $rules .= self::canonical_without_location( $cdnftp, '', $cors_header ); $rules .= "}\n"; break; } @@ -82,33 +86,6 @@ static public function canonical( $cdnftp = false ) { return $rules; } - - /** - * Returns allow-origin rules - * - * @param bool $cdnftp - * @return string - */ - static public function allow_origin( $cdnftp = false ) { - switch ( true ) { - case Util_Environment::is_apache(): - case Util_Environment::is_litespeed(): - $r = "\n"; - $r .= " Header set Access-Control-Allow-Origin \"*\"\n"; - $r .= "\n"; - - if ( !$cdnftp ) - return $r; - else - return - "\n" . - $r . - "\n"; - } - - return ''; - } - /** * Returns other mime types * diff --git a/Util_Ui.php b/Util_Ui.php index f37817b..e8e1cb2 100644 --- a/Util_Ui.php +++ b/Util_Ui.php @@ -531,7 +531,7 @@ static public function table_tr( $a ) { $a = apply_filters( 'w3tc_ui_settings_item', $a ); if ( isset( $a['style'] ) ) { - echo ''; } else { echo ' -
    - -
    -
    -
    -

    -

    -
    - -
    diff --git a/inc/lightbox/self_test.php b/inc/lightbox/self_test.php index 10d229a..5afec02 100644 --- a/inc/lightbox/self_test.php +++ b/inc/lightbox/self_test.php @@ -11,10 +11,9 @@

    - Installed: Functionality will work properly.', 'w3-total-cache' ); ?>
    - Not detected: May be installed, but cannot be automatically confirmed.', 'w3-total-cache' ); ?>
    - Ok: Current value is acceptable.', 'w3-total-cache' ); ?>
    - Yes / No: The value was successful detected.', 'w3-total-cache' ); ?> + Installed/Ok/Yes/True: Functionality will work properly.', 'w3-total-cache' ); ?>
    + Not detected/Not installed/Off: May be installed, but cannot be automatically confirmed. Functionality will be limmited.', 'w3-total-cache' ); ?>
    + Not Installed/Error/No/False: Plugin or some functions may not work.', 'w3-total-cache' ); ?>

    @@ -44,16 +43,16 @@ Microsoft IIS - Not detected + Not detected
  • FTP functions: - Installed + Installed - Not installed + Not installed FTP) CDN support)', 'w3-total-cache' ); ?>
  • @@ -61,9 +60,9 @@
  • - + - +
  • @@ -71,9 +70,9 @@
  • - + - +
  • @@ -81,9 +80,9 @@
  • zlib extension: - + - +
  • @@ -91,53 +90,53 @@
  • Opcode cache: - + - + - + - + = 6 ): ?> - + - +
  • - + - +
  • - + - +
  • - + - +
  • - + - +
  • @@ -145,11 +144,11 @@
  • - + - + - + CDN support)', 'w3-total-cache' ); ?>
  • @@ -157,11 +156,11 @@
  • - + - + - + CDN purge support)', 'w3-total-cache' ); ?>
  • @@ -169,27 +168,27 @@
  • - + - +
  • - + - +
  • - + - +
  • @@ -214,12 +213,12 @@ : - + - + - + @@ -254,15 +253,15 @@ : - + - + - + - + @@ -271,9 +270,9 @@
  • : - + - +
  • @@ -281,47 +280,49 @@ : - + - + - +
  • - + - +
  • - - () + + + + - +
  • - + - +
  • - () + () - +
  • diff --git a/inc/lightbox/support_us.php b/inc/lightbox/support_us.php index 11412fc..0388798 100644 --- a/inc/lightbox/support_us.php +++ b/inc/lightbox/support_us.php @@ -62,7 +62,11 @@ __( 'Tell Your Friends', 'w3-total-cache' ), $tweet_url, "btn w3tc-size image btn-default palette-twitter", - true ) ?> + true ); +echo Util_Ui::hidden( + __( 'tweeted' ), + __( 'tweeted' ), + '0' ) ?>
  • diff --git a/inc/lightbox/upgrade.php b/inc/lightbox/upgrade.php index d3b2501..4a795bb 100644 --- a/inc/lightbox/upgrade.php +++ b/inc/lightbox/upgrade.php @@ -18,14 +18,14 @@
    -
    • Full Site Delivery (FSD)
      - Provide the best user experience possible by enhancing by hosting HTML pages and RSS feeds with (supported) CDN's high speed global networks.
    • + Provide the best user experience possible by enhancing by hosting HTML pages and RSS feeds with (supported) CDN's high speed global networks.
    • Fragment Caching Module
      Unlocking the fragment caching module delivers enhanced performance for plugins and themes that use the WordPress Transient API. StudioPress' Genesis Framework is up to 60% faster with W3TC Pro.
    • diff --git a/inc/options/about.php b/inc/options/about.php index 7709b82..d175c33 100644 --- a/inc/options/about.php +++ b/inc/options/about.php @@ -18,7 +18,7 @@
    • HTTP compression of HTML, CSS, JavaScript and feeds', 'w3-total-cache' ); ?>
    • CDN) integration with Media Library, theme files and WordPress core', 'w3-total-cache' ); ?>
    • -
    • +
    • CDN (mirror only)', 'w3-total-cache' ); ?>
    • CSS and JavaScript in memory, on disk or on CDN', 'w3-total-cache' ); ?>
    • diff --git a/inc/options/cdn.php b/inc/options/cdn.php index 83bda18..840cba7 100644 --- a/inc/options/cdn.php +++ b/inc/options/cdn.php @@ -156,10 +156,11 @@ - + + +
      @@ -179,6 +182,19 @@ When SSL pages are returned no CDN URLs will appear in HTML pages. + + + + + + + diff --git a/inc/options/cdn/att.php b/inc/options/cdn/att.php index 19fbed3..f5d1407 100644 --- a/inc/options/cdn/att.php +++ b/inc/options/cdn/att.php @@ -27,14 +27,14 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/azure.php b/inc/options/cdn/azure.php index 96b08a4..75f0a4a 100644 --- a/inc/options/cdn/azure.php +++ b/inc/options/cdn/azure.php @@ -36,7 +36,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/cf.php b/inc/options/cdn/cf.php index 0e7882e..83eff93 100644 --- a/inc/options/cdn/cf.php +++ b/inc/options/cdn/cf.php @@ -26,12 +26,29 @@ - + @@ -41,7 +58,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/cf2.php b/inc/options/cdn/cf2.php index 79aa6d9..54153b1 100644 --- a/inc/options/cdn/cf2.php +++ b/inc/options/cdn/cf2.php @@ -41,7 +41,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/common/cnames.php b/inc/options/cdn/common/cnames.php index 9b1d1a8..6258fee 100644 --- a/inc/options/cdn/common/cnames.php +++ b/inc/options/cdn/common/cnames.php @@ -11,13 +11,22 @@ $cnames = array(''); } -$count = count($cnames); +$count = count( $cnames ); +if ( isset( $cnames['http_default'] ) ) + $count--; +if ( isset( $cnames['https_default'] ) ) + $count--; + +$real_index = 0; +foreach ( $cnames as $index => $cname ): + if ( $index === 'http_default' || $index === 'https_default' ) { + continue; + } -foreach ($cnames as $index => $cname): $label = ''; if ($count > 1): - switch ($index): + switch ($real_index): case 0: $label = __('(reserved for CSS)', 'w3-total-cache'); break; @@ -47,7 +56,7 @@ value="" style="display: none;" /> - + /> diff --git a/inc/options/cdn/cotendo.php b/inc/options/cdn/cotendo.php index 9e548b5..27171b6 100644 --- a/inc/options/cdn/cotendo.php +++ b/inc/options/cdn/cotendo.php @@ -34,14 +34,14 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/edgecast.php b/inc/options/cdn/edgecast.php index a3c7b0c..627a696 100644 --- a/inc/options/cdn/edgecast.php +++ b/inc/options/cdn/edgecast.php @@ -27,14 +27,14 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/ftp.php b/inc/options/cdn/ftp.php index 8811fcb..1229066 100644 --- a/inc/options/cdn/ftp.php +++ b/inc/options/cdn/ftp.php @@ -58,7 +58,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/maxcdn.php b/inc/options/cdn/maxcdn.php deleted file mode 100644 index cd9f1b5..0000000 --- a/inc/options/cdn/maxcdn.php +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/inc/options/cdn/mirror.php b/inc/options/cdn/mirror.php index e3c3274..fb0e9b6 100644 --- a/inc/options/cdn/mirror.php +++ b/inc/options/cdn/mirror.php @@ -13,14 +13,14 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/netdna.php b/inc/options/cdn/netdna.php deleted file mode 100644 index 3bdca43..0000000 --- a/inc/options/cdn/netdna.php +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/inc/options/cdn/rscf.php b/inc/options/cdn/rscf.php index 56aa56a..9fdfcc6 100644 --- a/inc/options/cdn/rscf.php +++ b/inc/options/cdn/rscf.php @@ -47,7 +47,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/s3.php b/inc/options/cdn/s3.php index 02f651b..ed1fafa 100644 --- a/inc/options/cdn/s3.php +++ b/inc/options/cdn/s3.php @@ -26,11 +26,29 @@ - + @@ -40,7 +58,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/cdn/s3_compatible.php b/inc/options/cdn/s3_compatible.php index a0d67d3..687f76c 100644 --- a/inc/options/cdn/s3_compatible.php +++ b/inc/options/cdn/s3_compatible.php @@ -42,7 +42,7 @@ -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> +
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' ); ?> diff --git a/inc/options/common/header.php b/inc/options/common/header.php index 6745298..21e077e 100644 --- a/inc/options/common/header.php +++ b/inc/options/common/header.php @@ -34,7 +34,7 @@ array( 'id' => 'browser_cache', 'text' => __( 'Browser Cache', 'w3-total-cache' ) ), array( 'id' => 'cdn', 'text' => __( 'CDN', 'w3-total-cache' ) ), array( 'id' => 'reverse_proxy', 'text' => __( 'Reverse Proxy', 'w3-total-cache' ) ) ) ); - if ( Util_Environment::is_w3tc_enterprise() ) + if ( Util_Environment::is_w3tc_pro() ) $anchors[] = array( 'id' => 'amazon_sns', 'text' => __( 'Amazon SNS', 'w3-total-cache' ) ); $anchors[] = array( 'id' => 'monitoring', 'text' => __( 'Monitoring', 'w3-total-cache' ) ); if ( $licensing_visible ) diff --git a/inc/options/dashboard.php b/inc/options/dashboard.php index f77064f..5a8a3d7 100644 --- a/inc/options/dashboard.php +++ b/inc/options/dashboard.php @@ -8,10 +8,10 @@

      - %s in %s%s mode.', 'w3-total-cache' ) + %s in %s mode.', 'w3-total-cache' ) , $enabled ? "enabled" : "disabled" , $enabled ? __( 'enabled', 'w3-total-cache' ) : __( 'disabled', 'w3-total-cache' ) - , Util_Environment::w3tc_edition( $this->_config ), ( Util_Environment::is_w3tc_edge( $this->_config ) ? __( ' edge', 'w3-total-cache' ) : '' ) ); + , Util_Environment::w3tc_edition( $this->_config ) ); ?>

      diff --git a/inc/options/general.php b/inc/options/general.php index 853f518..ea66ce0 100644 --- a/inc/options/general.php +++ b/inc/options/general.php @@ -16,14 +16,6 @@
      + checkbox( 'cdn.admin.media_library' ) ?>
      + All Media Library content will use CDN links on administration pages. +
      + checkbox( 'cdn.cors_header' ) ?> Add CORS header
      + Add CORS headers to allow cross-domain assets usage. +
      checkbox( 'cdn.reject.logged_roles' ) ?>
      diff --git a/inc/options/cdn/akamai.php b/inc/options/cdn/akamai.php index 1e0eec2..9b218f1 100644 --- a/inc/options/cdn/akamai.php +++ b/inc/options/cdn/akamai.php @@ -61,7 +61,7 @@
      _config->get_array( 'cdn.akamai.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> +
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>
      _config->get_array( 'cdn.att.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> +
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>
      - value="_config->get_string( 'cdn.cf.bucket' ) ); ?>" size="30" /> - type="button" value="" /> - + value="_config->get_string( 'cdn.cf.bucket' ) ) ); ?>" size="30" /> + + or + +
      _config->get_array( 'cdn.cotendo.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> +
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>
      _config->get_array( 'cdn.edgecast.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> +
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>
      - -
      - -
      - -
      - -
      - -
      - -
      - name="cdn__maxcdn__authorization_key" value="_config->get_string( 'cdn.maxcdn.authorization_key' ) ); ?>" size="60" onblur="w3tc_validate_cdn_key_result('maxcdn','')" /> - -
      -
      - -
      -
      - -
      - -
      - -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' )?> -
      - _config->get_array( 'cdn.maxcdn.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' )?> -
      - -
      _config->get_array( 'cdn.mirror.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?> +
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' ); ?>
      - -
      - -
      - -
      - -
      - -
      - -
      - name="cdn__netdna__authorization_key" value="_config->get_string( 'cdn.netdna.authorization_key' ) ); ?>" size="60" onblur="w3tc_validate_cdn_key_result('netdna','')" /> - -
      -
      - -
      -
      - -
      - -
      - -
      CDN providers may or may not support SSL, contact your vendor for more information.', 'w3-total-cache' )?> -
      - _config->get_array( 'cdn.netdna.domain' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?> -
      CDN provider, this value will replace your site\'s hostname in the HTML.', 'w3-total-cache' )?> -
      - -
      - value="_config->get_string( 'cdn.s3.bucket' ) ); ?>" size="30" /> - - + value="_config->get_string( 'cdn.s3.bucket' ) ) ); ?>" size="30" /> + + or + +
      - - -
      - -
      Preview mode: @@ -220,7 +212,7 @@ ) ); ?> - +
      @@ -329,7 +321,7 @@ ?> - +

      Allows policy management to be shared between a dynamic pool of servers. For example, each server in a pool to use opcode caching (which is not a shared resource) and purging is then syncronized between any number of servers in real-time; each server therefore behaves identically even though resources are not shared. @@ -430,6 +422,13 @@ class="w3tc-ignore-change" type="text" API key, visit the APIs Console. Go to the Project Home tab, activate the PageSpeed Insights API, and accept the Terms of Service. Then go to the API Access tab. The API key is in the Simple API Access section.', 'w3-total-cache' ); ?> + + + + +
      + Although not required, to prevent unauthorized use and quota theft, you have the option to restrict your key using a designated HTTP referrer. If you decide to use it, you will need to set this referrer within the API Console's "Http Referrers (web sites)" key restriction area (under Credentials). + - - - - _config ) ) - echo '' . - __( 'Enable Edge mode', 'w3-total-cache' ) . - ''; - else - echo '' . - __( 'Disable Edge mode', 'w3-total-cache' ) . - ''; -?> -
      - - - @@ -529,8 +507,9 @@ class="w3tc-ignore-change" type="text" checkbox_debug( array( 'fragmentcache', 'debug' ) ) ?>
      checkbox_debug( 'cdn.debug' ) ?>
      + checkbox_debug( 'cdnfsd.debug' ) ?>
      checkbox_debug( 'varnish.debug' ) ?>
      - + checkbox_debug( 'cluster.messagebus.debug' ) ?>
      HTML comment. View a page\'s source code to review.', 'w3-total-cache' ); ?> diff --git a/inc/options/minify.php b/inc/options/minify.php index 1198d9d..b735464 100644 --- a/inc/options/minify.php +++ b/inc/options/minify.php @@ -320,7 +320,7 @@ 'description' => __( 'For better performance, send files to browser before they are requested when using the HTTP/2 protocol.', 'w3-total-cache' ) . ( $this->_config->get_string( 'pgcache.engine' ) != 'file_generic' ? '' : - __( '
      Not supported by "Disk: Enhanced" page cache engine', 'w3-total-cache' ) ) + __( '
      Not supported by "Disk: Enhanced" page cache engine for Nginx', 'w3-total-cache' ) ) ) ); ?> @@ -445,7 +445,7 @@ 'description' => __( 'For better performance, send files to browser before they are requested when using the HTTP/2 protocol.', 'w3-total-cache' ) . ( $this->_config->get_string( 'pgcache.engine' ) != 'file_generic' ? '' : - __( '
      Not supported by "Disk: Enhanced" page cache engine', 'w3-total-cache' ) ) + __( '
      Not supported by "Disk: Enhanced" page cache engine for Nginx', 'w3-total-cache' ) ) ) ); ?> diff --git a/inc/options/minify/yuijs2.php b/inc/options/minify/yuijs2.php index c875710..42f7639 100644 --- a/inc/options/minify/yuijs2.php +++ b/inc/options/minify/yuijs2.php @@ -6,16 +6,16 @@ ?> - + - name="minify__yuijs__path__java" value="_config->get_string( 'minify.yuijs.path.java' ) ); ?>" size="100" /> - + - name="minify__yuijs__path__jar" value="_config->get_string( 'minify.yuijs.path.jar' ) ); ?>" size="100" /> @@ -27,9 +27,9 @@ - + - name="minify__yuijs__options__line-break" value="_config->get_integer( 'minify.yuijs.options.line-break' ) ); ?>" size="8" style="text-align: right;" /> diff --git a/inc/options/pgcache.php b/inc/options/pgcache.php index 48a8135..61a60bd 100644 --- a/inc/options/pgcache.php +++ b/inc/options/pgcache.php @@ -166,8 +166,8 @@ @@ -336,7 +336,7 @@
      - URLs with these query strings.', 'w3-total-cache' ); ?> + URLs that use these query string name-value pairs. The value part is not required. But if used, separate name-value pairs with an equals sign (i.e., name=value). Each pair should be on their own line.', 'w3-total-cache' ); ?> @@ -371,6 +371,42 @@ + + + + + + + + + + + + + + + + - _config->get_string( 'pgcache.engine' ) == 'file_generic' ): ?> + _config->get_string( 'pgcache.engine' ) == 'file_generic' ): ?> diff --git a/inc/popup/cdn_purge.php b/inc/popup/cdn_purge.php index 66bda62..782833f 100644 --- a/inc/popup/cdn_purge.php +++ b/inc/popup/cdn_purge.php @@ -8,7 +8,7 @@

      - + CDN by specifying the relative path on individual lines below and clicking the "Purge" button when done. For example:', 'w3-total-cache' ); ?>

      _config->get_string( 'cdn.engine' ) ): diff --git a/inc/widget/maxcdn_signup.php b/inc/widget/maxcdn_signup.php index aebfe15..524a8bd 100644 --- a/inc/widget/maxcdn_signup.php +++ b/inc/widget/maxcdn_signup.php @@ -18,34 +18,5 @@

      - - -

      - - - name="cdn.maxcdn.authorization_key" - value="_config->get_string( 'cdn.maxcdn.authorization_key' ) ); ?>" size="31" - onblur="w3tc_validate_cdn_key_result('maxcdn','')" /> -
      - - -

      - -

      - -
      -

      -

      - -
      - -

      -

      - - - + diff --git a/inc/widget/netdna.php b/inc/widget/netdna.php deleted file mode 100644 index 0841044..0000000 --- a/inc/widget/netdna.php +++ /dev/null @@ -1,64 +0,0 @@ - -
      -
      -
      -

      - ' . $account_status. '' ) ?> - ' . $content_zone . '' ) ?> -

      - -
      -
      - -
      -
      -

      -
        -
      • Transferred: %s', 'w3-total-cache' ), Util_Ui::format_bytes( $summary['size'] ) ) ?>
      • -
      • Cache Hits: %d (%d%%)', 'w3-total-cache' ), - $summary['cache_hit'], $summary['hit'] ? ( $summary['cache_hit']/$summary['hit'] )*100:$summary['hit'] ) ?>
      • -
      • Cache Misses (non-cache hits): %d (%d%%)', 'w3-total-cache' ), - $summary['noncache_hit'], $summary['hit']?( $summary['noncache_hit']/$summary['hit'] )*100:$summary['hit'] ) ?>
      • -
      -
      -
      -

      -
      -

      -

      - - -

      -
        - -
      • - -
      -
      -
      -
      diff --git a/inc/widget/netdna_signup.php b/inc/widget/netdna_signup.php deleted file mode 100644 index 2775382..0000000 --- a/inc/widget/netdna_signup.php +++ /dev/null @@ -1,53 +0,0 @@ - - diff --git a/ini/config-db-sample.php b/ini/config-db-sample.php new file mode 100644 index 0000000..f205c5b --- /dev/null +++ b/ini/config-db-sample.php @@ -0,0 +1,34 @@ +persistent = false; +$w3tc_dbcluster_config = array( + /** + * Persistent (bool) + * + * This determines whether to use mysql_connect or mysql_pconnect. The effects + * of this setting may vary and should be carefully tested. + * Default: false + */ + 'persistent' => false, -/** - * check_tcp_responsiveness - * - * Enables checking TCP responsiveness by fsockopen prior to mysql_connect or - * mysql_pconnect. This was added because PHP's mysql functions do not provide - * a variable timeout setting. Disabling it may improve average performance by - * a very tiny margin but lose protection against connections failing slowly. - * Default: true - */ -$wpdb_cluster->check_tcp_responsiveness = true; + /** + * check_tcp_responsiveness + * + * Enables checking TCP responsiveness by fsockopen prior to mysql_connect or + * mysql_pconnect. This was added because PHP's mysql functions do not provide + * a variable timeout setting. Disabling it may improve average performance by + * a very tiny margin but lose protection against connections failing slowly. + * Default: true + */ + 'check_tcp_responsiveness' => true, -/** - * Default is to always (reads & writes) use the master db when user is in administration backend. - * Set use_master_in_backend to false to disable this behavior. - * - * WARNING: if your cluster has any replication delays then when this is enabled, you may not see - * any admin changes until the replication catches up with the change written to your master - * server and will see old content/configuration until that point in time - You should test this - * in your environment fully. - */ -//$wpdb_cluster->use_master_in_backend = false; + /** + * Default is to always (reads & writes) use the master db when user is in administration backend. + * Set use_master_in_backend to false to disable this behavior. + * + * WARNING: if your cluster has any replication delays then when this is enabled, you may not see + * any admin changes until the replication catches up with the change written to your master + * server and will see old content/configuration until that point in time - You should test this + * in your environment fully. + */ + 'use_master_in_backend' => true, -/** - * This set the charset that the db connection should use. - * If DB_CHARSET is set there is no need to set $wpdb_cluster->charset. - */ -//$wpdb_cluster->charset = 'utf-8'; + /** + * This set the charset that the db connection should use. + * If DB_CHARSET is set there is no need to set $wpdb_cluster->charset. + */ + 'charset' => DB_CHARSET, -/** - * This set the charset that the db connection should use. - * If DB_COLLATE is set there is no need to set $wpdb_cluster->collate. - */ -//$wpdb_cluster->collate = 'utf8_general_ci'; -/** Configuration Functions **/ + /** + * This set the charset that the db connection should use. + * If DB_COLLATE is set there is no need to set $wpdb_cluster->collate. + */ + 'collate' => DB_COLLATE, + /** Configuration Functions **/ -/** - * $wpdb_cluster->add_database( $database ); - * - * $database is an associative array with these parameters: - * host (required) Hostname with optional :port. Default port is 3306. - * user (required) MySQL user name. - * password (required) MySQL user password. - * name (required) MySQL database name. - * read (optional) Whether server is readable. Default is 1 (readable). - * Also used to assign preference. - * write (optional) Whether server is writable. Default is 1 (writable). - * Also used to assign preference in multi-master mode. - * dataset (optional) Name of dataset. Default is 'global'. - * timeout (optional) Seconds to wait for TCP responsiveness. Default is 0.2 - * connect_function (optional) connection function to use - * zone (optional) name of zone where server is located. - * Used for web applications hosted on cluster - */ + /** + * database is an associative array with these parameters: + * host (required) Hostname with optional :port. Default port is 3306. + * user (required) MySQL user name. + * password (required) MySQL user password. + * name (required) MySQL database name. + * read (optional) Whether server is readable. Default is 1 (readable). + * Also used to assign preference. + * write (optional) Whether server is writable. Default is 1 (writable). + * Also used to assign preference in multi-master mode. + * dataset (optional) Name of dataset. Default is 'global'. + * timeout (optional) Seconds to wait for TCP responsiveness. Default is 0.2 + * connect_function (optional) connection function to use + * zone (optional) name of zone where server is located. + * Used for web applications hosted on cluster + * + * Read & write + * A database definition can include 'read' and 'write' parameters. These + * operate as boolean switches but they are typically specified as integers. + * They allow or disallow use of the database for reading or writing. + * + * A master database might be configured to allow reading and writing: + * 'write' => true, + * 'read' => true, + * while a slave would be allowed only to read: + * 'write' => false, + * 'read' => true, + * + * It might be advantageous to disallow reading from the master, such as when + * there are many slaves available and the master is very busy with writes. + * 'write' => true, + * 'read' => false, + * + */ + 'databases' => array( + 'master' => array( + 'host' => DB_HOST, // If port is other than 3306, use host:port. + 'user' => DB_USER, + 'password' => DB_PASSWORD, + 'name' => DB_NAME + ) + ), -/** - * $wpdb_cluster->add_callback( $callback, $callback_group = 'dataset' ); - * - * $callback is a callable function or method. $callback_group is the - * group of callbacks, this $callback belongs to. - * - * Callbacks are executed in the order in which they are registered until one - * of them returns something other than null. - * - * The default $callback_group is 'dataset'. Callback in this group - * will be called with two arguments and expected to compute a dataset or return null. - * $dataset = $callback($table, &$wpdb); - * - */ + /** + * Zones (for web applications hosted on cluster) + * + * When your databases are located in separate physical locations there is + * typically an advantage to connecting to a nearby server instead of a more + * distant one. This can be configured by defining zones. + * + * Add 'zone' parameter to add_server call: + * 'zone' => 'A' + * + * Plugin determines where application is running by checking + * $_SERVER['SERVER_NAME'] system variable against defined in zone definition + * and then connects to servers following defined order: + * Value '*' can be used as 'server_names' item to indicate any server. + * + * 'zones' => array( + * 'zone_name' => array( + * 'server_names' => array('host1', 'host1.1'), + * 'zone_priorities' => array('A', 'B') + * ) + * ) + * + * As a result it will try to connect to servers in zone A first, then servers + * in zone B. + */ + 'zones' => array( + ), -/** Masters and slaves - * - * A database definition can include 'read' and 'write' parameters. These - * operate as boolean switches but they are typically specified as integers. - * They allow or disallow use of the database for reading or writing. - * - * A master database might be configured to allow reading and writing: - * 'write' => true, - * 'read' => true, - * while a slave would be allowed only to read: - * 'write' => false, - * 'read' => true, - * - * It might be advantageous to disallow reading from the master, such as when - * there are many slaves available and the master is very busy with writes. - * 'write' => true, - * 'read' => false, - */ + /** + * Filters + * + * Filters are executed on event in the order in which they are + * registered until one of them returns something other than null. + * + * Tags: + * 'dataset' - fitler will be called with two arguments and expected to + * return a dataset to connect to + * $dataset = $callback($table, &$wpdb); + * 'blog_dataset' - fitler will be called with argument $blog_id and + * expected to return a dataset to connect to. + * Used when data are partitioned at blog level + * $dataset = $callback($blog_id); + * + * 'filters' => array( + * array( + * 'tag' => 'tag_to_add', + * 'function_to_add' => 'function_name' + + * ); + */ + 'filters' => array( + ) +); -/** - * Web applications hosted on cluster - * - * When your databases are located in separate physical locations there is - * typically an advantage to connecting to a nearby server instead of a more - * distant one. This can be configured by defining zones. - * - * Add 'zone' parameter to add_server call: - * 'zone' => 'A' - * - * Plugin determines where application is running by checking - * $_SERVER['SERVER_NAME'] system variable against defined in zone definition - * and then connects to servers following defined order: - * Value '*' can be used as 'server_names' item to indicate any server. - * - * $wpdb_cluster->add_zone(array( - * 'name' => 'A', - * 'server_names' => array('host1', 'host1.1'), - * 'zone_priorities' => array('A', 'B') - * )); - * - * As a result it will try to connect to servers in zone A first, then servers - * in zone B. - */ -/** - * This is the most basic way to add a server using only the - * required parameters: host, user, password, name. - * This adds the DB defined in wp-config.php as a read/write server for - * the 'global' dataset. (Every table is in 'global' by default.) - */ -$wpdb_cluster->add_database(array( - 'host' => DB_HOST, // If port is other than 3306, use host:port. - 'user' => DB_USER, - 'password' => DB_PASSWORD, - 'name' => DB_NAME, -)); /** * This adds the same server again, only this time it is configured as a slave. * The last three parameters are set to the defaults but are shown for clarity. */ /* -$wpdb_cluster->add_database(array( - 'host' => DB_HOST, // If port is other than 3306, use host:port. - 'user' => DB_USER, - 'password' => DB_PASSWORD, - 'name' => DB_NAME, - 'write' => false, - 'read' => true, - 'dataset' => 'global', - 'timeout' => 0.2, +$w3tc_dbcluster_config = array( + 'databases' => array( + 'master' => array( + 'host' => DB_HOST, + 'user' => DB_USER, + 'password' => DB_PASSWORD, + 'name' => DB_NAME + ), + 'slave' => array( + 'host' => DB_HOST, + 'user' => DB_USER, + 'password' => DB_PASSWORD, + 'name' => DB_NAME, + 'write' => false, + 'read' => true, + 'timeout' => 0.2, )); */ @@ -161,20 +177,30 @@ * separated from the global dataset. */ /* -$wpdb_cluster->add_database(array( - 'host' => 'global.db.example.com', - 'user' => 'globaluser', - 'password' => 'globalpassword', - 'name' => 'globaldb', -)); -$wpdb_cluster->add_database(array( - 'host' => 'blog.db.example.com', - 'user' => 'bloguser', - 'password' => 'blogpassword', - 'name' => 'blogdb', - 'dataset' => 'blog2', -)); -$wpdb_cluster->add_callback('my_db_callback', 'blog_dataset'); +$w3tc_dbcluster_config = array( + 'databases' => array( + 'master' => array( + 'host' => 'global.db.example.com', + 'user' => 'globaluser', + 'password' => 'globalpassword', + 'name' => 'globaldb', + ), + 'partition2' => array( + 'host' => 'blog.db.example.com', + 'user' => 'bloguser', + 'password' => 'blogpassword', + 'name' => 'blogdb', + 'dataset' => 'blog2', + ), + ) + 'filters' => array( + array( + 'tag' => 'blog_dataset', + 'function_to_add' => 'my_db_callback' + ) + ) +); + function my_db_callback($blog_id, $wpdb_cluster) { if ($blog_id > 5)) return 'blog2'; diff --git a/ini/web.config b/ini/web.config index 31fdf83..826dd9d 100644 --- a/ini/web.config +++ b/ini/web.config @@ -3,8 +3,33 @@ + + + + + + + + + + + + + + + + + + + + + + + - + @@ -12,10 +37,9 @@ - + - @@ -29,6 +53,7 @@ + diff --git a/languages/faq-en_US.xml b/languages/faq-en_US.xml index 7325e64..828c6dc 100644 --- a/languages/faq-en_US.xml +++ b/languages/faq-en_US.xml @@ -251,6 +251,11 @@ Note: To disable page caching of specific theme templates or plugin files you ne Enable "Cache feeds: site, categories, tags, comments" on the "Page Cache" settings tab. Some sitemap implementations are considered by WordPress to be "feeds."

      ]]>
      + + + Yes, but you should note that many types of requests can be made prior to the initialization of the database, so switching to database-stored configuration will increase request response time. This functionality should be used only when you have no other option. To enable database-stored setting, add define( 'W3TC_CONFIG_DATABASE', true ); to your wp-config.php and the plugin will immediately begin to use options table for configuration storage via wp-options table. Alternatively, W3TC_CONFIG_DATABASE_TABLE constant can be defined to specify a table you created specifically for this use case.

      Note: If you customized your settings prior to switching to the database storage setting, you will lose your configuration settings. Be sure to export settings beforehand, then import afterward. You can then remove configuration file from your source control if it was there and delete it. Ideally, defining the W3TC_CONFIG_DATABASE constant prior plugin activation allows you to skip all of these steps.

      ]]>
      + +
      @@ -407,7 +412,7 @@ Note: To disable page caching of specific theme templates or plugin files you ne CDN? ]]> First create an account. Next, in the "Content Delivery Network" section of the "General Settings" tab, select Rackspace Cloud Files as the "CDN Type." Now, in the "Configuration" section of the "Content Delivery Network" tab, enter the "Username" and "API key" associated with your account (found in the API Access section of the rackspace cloud control panel) in the respective fields. Next enter a name for the container to use (avoid special characters and spaces). If the operation is successful, the container's ID will automatically appear in the "Replace site's hostname with" field. You may optionally, specify the container name and container ID of an existing container if you wish. Click the "Test Cloud Files Upload" button and make sure that the test is successful, if not check your settings and try again. Save your settings. You're now ready to export your media library, theme and any other files to the CDN.

      +

      First create an account. Next, in the "Content Delivery Network" section of the "General Settings" tab, select Rackspace Cloud Files as the "CDN Type." Now, in the "Configuration" section of the "Content Delivery Network" tab, enter the "Username" and "API key" associated with your account (found in the API Access section of the rackspace cloud control panel) in the respective fields. Next enter a name for the container to use (avoid special characters and spaces). If the operation is successful, the container's ID will automatically appear in the "Replace site's hostname with" field. You may optionally, specify the container name and container ID of an existing container if you wish. Click the "Test Cloud Files Upload" button and make sure that the test is successful, if not check your settings and try again. Save your settings. You're now ready to export your media library, theme and any other files to the CDN.

      You may optionally, specify up to 10 hostnames to use rather than the default hostname, doing so will improve the render performance of your site's pages.

      Now go to the General tab and click the "Enable" checkbox and save the settings to enable CDN functionality and empty the cache for the changes to take effect. If preview mode is active you will need to "deploy" your changes for them to take effect.

      ]]>
      diff --git a/languages/faq-pro-en_US.xml b/languages/faq-pro-en_US.xml index 38c30e5..7def868 100644 --- a/languages/faq-pro-en_US.xml +++ b/languages/faq-pro-en_US.xml @@ -32,7 +32,7 @@
        -
      1. Select CDN provider of your choice in a "Full site mirroring" group of "CDN type" dropdown on "General Settings" page, select MaxCDN there.
      2. +
      3. Select CDN provider of your choice in a "Full site mirroring" group of "CDN type" dropdown on "General Settings" page, select MaxCDN there.
      4. Go to "CDN" page.
      5. Click "Authorize" button
      6. Type in API key of your account. You can obtain by following a link in a popup. Opening that link will you will be prompted for your MaxCDN login and password. @@ -50,7 +50,7 @@
          -
        1. Select CDN provider of your choice in a "Full site mirroring" group of "CDN type" dropdown on "General Settings" page, select CloudFront there.
        2. +
        3. Select CDN provider of your choice in a "Full site mirroring" group of "CDN type" dropdown on "General Settings" page, select CloudFront there.
        4. Go to "CDN" page.
        5. Click "Authorize" button
        6. Type in Access Key and Secret Key of your account.
        7. @@ -102,8 +102,8 @@
        8. Add new CNAME recored for example.com that points to the Domain Name that belongs to the distribution you created previously.

        How to configure W3 Total Cache:

        -

        There are two methods to configure W3 Total Cache CDN. Origin Pull "CloudFront" or "Generic mirror". If you want to be able to invalidate URLs from within WordPress you need to use the CloudFront option. - If you do not configure an CDN the wrong URLs will be used when linking to CSS, JS and other files. +

        There are two methods to configure W3 Total Cache CDN. Origin Pull "CloudFront" or "Generic mirror". If you want to be able to invalidate URLs from within WordPress you need to use the CloudFront option. + If you do not configure an CDN the wrong URLs will be used when linking to CSS, JS and other files.

        Configure CloudFront:

          diff --git a/languages/w3-total-cache.pot b/languages/w3-total-cache.pot index 9f652c0..5e12704 100644 --- a/languages/w3-total-cache.pot +++ b/languages/w3-total-cache.pot @@ -537,7 +537,7 @@ msgid "Update via FTP" msgstr "" #: inc/functions/admin_ui.php:73 inc/functions/widgets.php:61 -#: inc/lightbox/edge.php:15 inc/options/support/form.php:26 +#: inc/options/support/form.php:26 #: inc/options/support/payment.php:21 msgid "Cancel" msgstr "" @@ -655,23 +655,6 @@ msgstr "" msgid "Something that describes your zone. Length: 1-255 chars" msgstr "" -#: inc/lightbox/edge.php:7 -msgid "" -"You can now keep up-to-date on all of the web performance optimization (WPO) " -"techniques that will make your website as fast as possible without having to " -"worry about new features breaking your website when you update!" -msgstr "" - -#: inc/lightbox/edge.php:8 -msgid "" -"Enable \"edge mode\" to opt-in to pre-release features or simply close this " -"window to opt-in to bug fixes, security fixes and settings updates only." -msgstr "" - -#: inc/lightbox/edge.php:13 -msgid "Enable Edge Mode" -msgstr "" - #: inc/lightbox/minify_recommendations.php:5 msgid "" "To get started with minify, we've identified the following external CSS and " @@ -2904,14 +2887,6 @@ msgstr "" msgid "Disable Edge mode" msgstr "" -#: inc/options/general.php:653 -msgid "" -"Enable this to try out new functionality under development. Might cause " -"issues on some sites. If you have issues and can't access wp-admin, remove " -"\"define('W3TC_EDGE_MODE', true);\" from your wp-config.php file and edge " -"mode features will be disabled." -msgstr "" - #: inc/options/general.php:666 msgid "" "Detailed information about each cache will be appended in (publicly " diff --git a/lib/Minify/CSSmin.php b/lib/Minify/CSSmin.php deleted file mode 100644 index b4cac4f..0000000 --- a/lib/Minify/CSSmin.php +++ /dev/null @@ -1,758 +0,0 @@ -memory_limit = 128 * 1048576; // 128MB in bytes - $this->max_execution_time = 60; // 1 min - $this->pcre_backtrack_limit = 1000 * 1000; - $this->pcre_recursion_limit = 500 * 1000; - - $this->raise_php_limits = (bool) $raise_php_limits; - } - - /** - * Minify a string of CSS - * @param string $css - * @param int|bool $linebreak_pos - * @return string - */ - public function run($css = '', $linebreak_pos = FALSE) - { - if (empty($css)) { - return ''; - } - - if ($this->raise_php_limits) { - $this->do_raise_php_limits(); - } - - $this->comments = array(); - $this->preserved_tokens = array(); - - $start_index = 0; - $length = strlen($css); - - $css = $this->extract_data_urls($css); - - // collect all comment blocks... - while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) { - $end_index = $this->index_of($css, '*/', $start_index + 2); - if ($end_index < 0) { - $end_index = $length; - } - $comment_found = $this->str_slice($css, $start_index + 2, $end_index); - $this->comments[] = $comment_found; - $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___'; - $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index); - // Set correct start_index: Fixes issue #2528130 - $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found); - } - - // preserve strings so their content doesn't get accidentally minified - $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css); - - // Let's divide css code in chunks of 25.000 chars aprox. - // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit" - // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really - // long strings and a (sub)pattern matches a number of chars greater than - // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently - // returning NULL and $css would be empty. - $charset = ''; - $charset_regexp = '/(@charset)( [^;]+;)/i'; - $css_chunks = array(); - $css_chunk_length = 25000; // aprox size, not exact - $start_index = 0; - $i = $css_chunk_length; // save initial iterations - $l = strlen($css); - - - // if the number of characters is 25000 or less, do not chunk - if ($l <= $css_chunk_length) { - $css_chunks[] = $css; - } else { - // chunk css code securely - while ($i < $l) { - $i += 50; // save iterations. 500 checks for a closing curly brace } - if ($l - $start_index <= $css_chunk_length || $i >= $l) { - $css_chunks[] = $this->str_slice($css, $start_index); - break; - } - if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) { - // If there are two ending curly braces }} separated or not by spaces, - // join them in the same chunk (i.e. @media blocks) - $next_chunk = substr($css, $i); - if (preg_match('/^\s*\}/', $next_chunk)) { - $i = $i + $this->index_of($next_chunk, '}') + 1; - } - - $css_chunks[] = $this->str_slice($css, $start_index, $i); - $start_index = $i; - } - } - } - - // Minify each chunk - for ($i = 0, $n = count($css_chunks); $i < $n; $i++) { - $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos); - // Keep the first @charset at-rule found - if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) { - $charset = strtolower($matches[1]) . $matches[2]; - } - // Delete all @charset at-rules - $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]); - } - - // Update the first chunk and push the charset to the top of the file. - $css_chunks[0] = $charset . $css_chunks[0]; - - return implode('', $css_chunks); - } - - /** - * Sets the memory limit for this script - * @param int|string $limit - */ - public function set_memory_limit($limit) - { - $this->memory_limit = $this->normalize_int($limit); - } - - /** - * Sets the maximum execution time for this script - * @param int|string $seconds - */ - public function set_max_execution_time($seconds) - { - $this->max_execution_time = (int) $seconds; - } - - /** - * Sets the PCRE backtrack limit for this script - * @param int $limit - */ - public function set_pcre_backtrack_limit($limit) - { - $this->pcre_backtrack_limit = (int) $limit; - } - - /** - * Sets the PCRE recursion limit for this script - * @param int $limit - */ - public function set_pcre_recursion_limit($limit) - { - $this->pcre_recursion_limit = (int) $limit; - } - - /** - * Try to configure PHP to use at least the suggested minimum settings - */ - private function do_raise_php_limits() - { - $php_limits = array( - 'memory_limit' => $this->memory_limit, - 'max_execution_time' => $this->max_execution_time, - 'pcre.backtrack_limit' => $this->pcre_backtrack_limit, - 'pcre.recursion_limit' => $this->pcre_recursion_limit - ); - - // If current settings are higher respect them. - foreach ($php_limits as $name => $suggested) { - $current = $this->normalize_int(ini_get($name)); - // memory_limit exception: allow -1 for "no memory limit". - if ($current > -1 && ($suggested == -1 || $current < $suggested)) { - ini_set($name, $suggested); - } - } - } - - /** - * Does bulk of the minification - * @param string $css - * @param int|bool $linebreak_pos - * @return string - */ - private function minify($css, $linebreak_pos) - { - // strings are safe, now wrestle the comments - for ($i = 0, $max = count($this->comments); $i < $max; $i++) { - - $token = $this->comments[$i]; - $placeholder = '/' . self::COMMENT . $i . '___/'; - - // ! in the first position of the comment means preserve - // so push to the preserved tokens keeping the ! - if (substr($token, 0, 1) === '!') { - $this->preserved_tokens[] = $token; - $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___'; - $css = preg_replace($placeholder, $token_tring, $css, 1); - // Preserve new lines for /*! important comments - $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css); - $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css); - continue; - } - - // \ in the last position looks like hack for Mac/IE5 - // shorten that to /*\*/ and the next one to /**/ - if (substr($token, (strlen($token) - 1), 1) === '\\') { - $this->preserved_tokens[] = '\\'; - $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); - $i = $i + 1; // attn: advancing the loop - $this->preserved_tokens[] = ''; - $css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); - continue; - } - - // keep empty comments after child selectors (IE7 hack) - // e.g. html >/**/ body - if (strlen($token) === 0) { - $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1)); - if ($start_index > 2) { - if (substr($css, $start_index - 3, 1) === '>') { - $this->preserved_tokens[] = ''; - $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); - } - } - } - - // in all other cases kill the comment - $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1); - } - - - // Normalize all whitespace strings to single spaces. Easier to work with that way. - $css = preg_replace('/\s+/', ' ', $css); - - // Shorten & preserve calculations calc(...) since spaces are important - $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css); - - // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed - // +1.2em to 1.2em, +.8px to .8px, +2% to 2% - $css = preg_replace('/((? -9.0 to -9 - $css = preg_replace('/((?\+\(\)\]\~\=,])/', '$1', $css); - - // Restore spaces for !important - $css = preg_replace('/\!important/i', ' !important', $css); - - // bring back the colon - $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css); - - // retain space for special IE6 cases - $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css); - - // no space after the end of a preserved comment - $css = preg_replace('/\*\/ /', '*/', $css); - - // lowercase some popular @directives - $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css); - - // lowercase some more common pseudo-elements - $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css); - - // lowercase some more common functions - $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css); - - // lower case some common function that can be values - // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us - $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css); - - // Put the space back in some cases, to support stuff like - // @media screen and (-webkit-min-device-pixel-ratio:0){ - $css = preg_replace('/\band\(/i', 'and (', $css); - - // Remove the spaces after the things that should not have spaces after them. - $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css); - - // remove unnecessary semicolons - $css = preg_replace('/;+\}/', '}', $css); - - // Fix for issue: #2528146 - // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack) - // to avoid issues on Symbian S60 3.x browsers. - $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css); - - // Replace 0 length units 0(px,em,%) with 0. - $css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css); - - // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0. - $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css); - - // Fix for issue: #2528142 - // Replace text-shadow:0; with text-shadow:0 0 0; - $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css); - - // Replace background-position:0; with background-position:0 0; - // same for transform-origin - // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center) - $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css); - - // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space) - // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space) - // This makes it more likely that it'll get further compressed in the next step. - $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css); - $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css); - - // Shorten colors from #AABBCC to #ABC or short color name. - $css = $this->compress_hex_colors($css); - - // border: none to border:0, outline: none to outline:0 - $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css); - - // shorter opacity IE filter - $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css); - - // Find a fraction that is used for Opera's -o-device-pixel-ratio query - // Add token to add the "\" back in later - $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css); - - // Remove empty rules. - $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css); - - // Add "/" back to fix Opera -o-device-pixel-ratio query - $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css); - - // Some source control tools don't like it when files containing lines longer - // than, say 8000 characters, are checked in. The linebreak option is used in - // that case to split long lines after a specific column. - if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) { - $linebreak_pos = (int) $linebreak_pos; - $start_index = $i = 0; - while ($i < strlen($css)) { - $i++; - if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) { - $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i); - $start_index = $i; - } - } - } - - // Replace multiple semi-colons in a row by a single one - // See SF bug #1980989 - $css = preg_replace('/;;+/', ';', $css); - - // Restore new lines for /*! important comments - $css = preg_replace('/'. self::NL .'/', "\n", $css); - - // Lowercase all uppercase properties - $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css); - - // restore preserved comments and strings - for ($i = 0, $max = count($this->preserved_tokens); $i < $max; $i++) { - $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1); - } - - // Trim the final string (for any leading or trailing white spaces) - return trim($css); - } - - /** - * Utility method to replace all data urls with tokens before we start - * compressing, to avoid performance issues running some of the subsequent - * regexes against large strings chunks. - * - * @param string $css - * @return string - */ - private function extract_data_urls($css) - { - // Leave data urls alone to increase parse performance. - $max_index = strlen($css) - 1; - $append_index = $index = $last_index = $offset = 0; - $sb = array(); - $pattern = '/url\(\s*(["\']?)data\:/i'; - - // Since we need to account for non-base64 data urls, we need to handle - // ' and ) being part of the data string. Hence switching to indexOf, - // to determine whether or not we have matching string terminators and - // handling sb appends directly, instead of using matcher.append* methods. - - while (preg_match($pattern, $css, $m, 0, $offset)) { - $index = $this->index_of($css, $m[0], $offset); - $last_index = $index + strlen($m[0]); - $start_index = $index + 4; // "url(".length() - $end_index = $last_index - 1; - $terminator = $m[1]; // ', " or empty (not quoted) - $found_terminator = FALSE; - - if (strlen($terminator) === 0) { - $terminator = ')'; - } - - while ($found_terminator === FALSE && $end_index+1 <= $max_index) { - $end_index = $this->index_of($css, $terminator, $end_index + 1); - - // endIndex == 0 doesn't really apply here - if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') { - $found_terminator = TRUE; - if (')' != $terminator) { - $end_index = $this->index_of($css, ')', $end_index); - } - } - } - - // Enough searching, start moving stuff over to the buffer - $sb[] = $this->str_slice($css, $append_index, $index); - - if ($found_terminator) { - $token = $this->str_slice($css, $start_index, $end_index); - $token = preg_replace('/\s+/', '', $token); - $this->preserved_tokens[] = $token; - - $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)'; - $sb[] = $preserver; - - $append_index = $end_index + 1; - } else { - // No end terminator found, re-add the whole match. Should we throw/warn here? - $sb[] = $this->str_slice($css, $index, $last_index); - $append_index = $last_index; - } - - $offset = $last_index; - } - - $sb[] = $this->str_slice($css, $append_index); - - return implode('', $sb); - } - - /** - * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name. - * - * DOES NOT compress CSS ID selectors which match the above pattern (which would break things). - * e.g. #AddressForm { ... } - * - * DOES NOT compress IE filters, which have hex color values (which would break things). - * e.g. filter: chroma(color="#FFFFFF"); - * - * DOES NOT compress invalid hex values. - * e.g. background-color: #aabbccdd - * - * @param string $css - * @return string - */ - private function compress_hex_colors($css) - { - // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters) - $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS'; - $_index = $index = $last_index = $offset = 0; - $sb = array(); - // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors - $short_safe = array( - '#808080' => 'gray', - '#008000' => 'green', - '#800000' => 'maroon', - '#000080' => 'navy', - '#808000' => 'olive', - '#ffa500' => 'orange', - '#800080' => 'purple', - '#c0c0c0' => 'silver', - '#008080' => 'teal', - '#f00' => 'red' - ); - - while (preg_match($pattern, $css, $m, 0, $offset)) { - $index = $this->index_of($css, $m[0], $offset); - $last_index = $index + strlen($m[0]); - $is_filter = $m[1] !== null && $m[1] !== ''; - - $sb[] = $this->str_slice($css, $_index, $index); - - if ($is_filter) { - // Restore, maintain case, otherwise filter will break - $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]; - } else { - if (strtolower($m[2]) == strtolower($m[3]) && - strtolower($m[4]) == strtolower($m[5]) && - strtolower($m[6]) == strtolower($m[7])) { - // Compress. - $hex = '#' . strtolower($m[3] . $m[5] . $m[7]); - } else { - // Non compressible color, restore but lower case. - $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]); - } - // replace Hex colors to short safe color names - $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex; - } - - $_index = $offset = $last_index - strlen($m[8]); - } - - $sb[] = $this->str_slice($css, $_index); - - return implode('', $sb); - } - - /* CALLBACKS - * --------------------------------------------------------------------------------------------- - */ - - private function replace_string($matches) - { - $match = $matches[0]; - $quote = substr($match, 0, 1); - // Must use addcslashes in PHP to avoid parsing of backslashes - $match = addcslashes($this->str_slice($match, 1, -1), '\\'); - - // maybe the string contains a comment-like substring? - // one, maybe more? put'em back then - if (($pos = $this->index_of($match, self::COMMENT)) >= 0) { - for ($i = 0, $max = count($this->comments); $i < $max; $i++) { - $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1); - } - } - - // minify alpha opacity in filter strings - $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match); - - $this->preserved_tokens[] = $match; - return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote; - } - - private function replace_colon($matches) - { - return preg_replace('/\:/', self::CLASSCOLON, $matches[0]); - } - - private function replace_calc($matches) - { - $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2])); - return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')'; - } - - private function rgb_to_hex($matches) - { - // Support for percentage values rgb(100%, 0%, 45%); - if ($this->index_of($matches[1], '%') >= 0){ - $rgbcolors = explode(',', str_replace('%', '', $matches[1])); - for ($i = 0; $i < count($rgbcolors); $i++) { - $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55); - } - } else { - $rgbcolors = explode(',', $matches[1]); - } - - // Values outside the sRGB color space should be clipped (0-255) - for ($i = 0; $i < count($rgbcolors); $i++) { - $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255); - $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]); - } - - // Fix for issue #2528093 - if (!preg_match('/[\s\,\);\}]/', $matches[2])){ - $matches[2] = ' ' . $matches[2]; - } - - return '#' . implode('', $rgbcolors) . $matches[2]; - } - - private function hsl_to_hex($matches) - { - $values = explode(',', str_replace('%', '', $matches[1])); - $h = floatval($values[0]); - $s = floatval($values[1]); - $l = floatval($values[2]); - - // Wrap and clamp, then fraction! - $h = ((($h % 360) + 360) % 360) / 360; - $s = $this->clamp_number($s, 0, 100) / 100; - $l = $this->clamp_number($l, 0, 100) / 100; - - if ($s == 0) { - $r = $g = $b = $this->round_number(255 * $l); - } else { - $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l); - $v1 = (2 * $l) - $v2; - $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3))); - $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h)); - $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3))); - } - - return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2])); - } - - private function lowercase_pseudo_first($matches) - { - return ':first-'. strtolower($matches[1]) .' '. $matches[2]; - } - - private function lowercase_directives($matches) - { - return '@'. strtolower($matches[1]); - } - - private function lowercase_pseudo_elements($matches) - { - return ':'. strtolower($matches[1]); - } - - private function lowercase_common_functions($matches) - { - return ':'. strtolower($matches[1]) .'('; - } - - private function lowercase_common_functions_values($matches) - { - return $matches[1] . strtolower($matches[2]); - } - - private function lowercase_properties($matches) - { - return $matches[1].strtolower($matches[2]).$matches[3]; - } - - /* HELPERS - * --------------------------------------------------------------------------------------------- - */ - - private function hue_to_rgb($v1, $v2, $vh) - { - $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh); - if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh; - if ($vh * 2 < 1) return $v2; - if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6; - return $v1; - } - - private function round_number($n) - { - return intval(floor(floatval($n) + 0.5), 10); - } - - private function clamp_number($n, $min, $max) - { - return min(max($n, $min), $max); - } - - /** - * PHP port of Javascript's "indexOf" function for strings only - * Author: Tubal Martin http://blog.margenn.com - * - * @param string $haystack - * @param string $needle - * @param int $offset index (optional) - * @return int - */ - private function index_of($haystack, $needle, $offset = 0) - { - $index = strpos($haystack, $needle, $offset); - - return ($index !== FALSE) ? $index : -1; - } - - /** - * PHP port of Javascript's "slice" function for strings only - * Author: Tubal Martin http://blog.margenn.com - * Tests: http://margenn.com/tubal/str_slice/ - * - * @param string $str - * @param int $start index - * @param int|bool $end index (optional) - * @return string - */ - private function str_slice($str, $start = 0, $end = FALSE) - { - if ($end !== FALSE && ($start < 0 || $end <= 0)) { - $max = strlen($str); - - if ($start < 0) { - if (($start = $max + $start) < 0) { - return ''; - } - } - - if ($end < 0) { - if (($end = $max + $end) < 0) { - return ''; - } - } - - if ($end <= $start) { - return ''; - } - } - - $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start); - return ($slice === FALSE) ? '' : $slice; - } - - /** - * Convert strings like "64M" or "30" to int values - * @param mixed $size - * @return int - */ - private function normalize_int($size) - { - if (is_string($size)) { - switch (substr($size, -1)) { - case 'M': case 'm': return $size * 1048576; - case 'K': case 'k': return $size * 1024; - case 'G': case 'g': return $size * 1073741824; - } - } - - return (int) $size; - } -} \ No newline at end of file diff --git a/lib/Minify/Minify/CSS/UriRewriter.php b/lib/Minify/Minify/CSS/UriRewriter.php index b7b9df1..cc7ca54 100644 --- a/lib/Minify/Minify/CSS/UriRewriter.php +++ b/lib/Minify/Minify/CSS/UriRewriter.php @@ -434,11 +434,10 @@ private static function _processUriCB($m) if (preg_match('~\.([a-z-_]+)(\?.*)?$~', $uri, $matches)) { $extension = $matches[1]; - $query = (isset($matches[2]) ? $matches[2] : ''); if ($extension && in_array($extension, self::$_browserCacheExtensions)) { $uri = \W3TC\Util_Environment::remove_query($uri); - $uri .= ($query ? '&' : '?') . self::$_browserCacheId; + $uri .= ( strpos( $uri, '?' ) !== false ? '&' : '?' ) . self::$_browserCacheId; } } } diff --git a/lib/Minify/Minify/Cache/File.php b/lib/Minify/Minify/Cache/File.php index d22d66e..80c8164 100644 --- a/lib/Minify/Minify/Cache/File.php +++ b/lib/Minify/Minify/Cache/File.php @@ -48,7 +48,7 @@ public function store($id, $data) // retry with make dir \W3TC\Util_File::mkdir_from_safe(dirname($path), W3TC_CACHE_DIR); - if (!@file_put_contents($path, $data, $flag)) + if (!@file_put_contents($path, $data['content'], $flag)) return false; } @@ -152,7 +152,8 @@ public function fetch($id) @flock($fp, LOCK_UN); @fclose($fp); - return $ret; + $data['content'] = $ret; + return $data; } } else { $data['content'] = @file_get_contents($path); diff --git a/lib/Minify/Minify/ClosureCompiler.php b/lib/Minify/Minify/ClosureCompiler.php index 1fad320..b2dde43 100644 --- a/lib/Minify/Minify/ClosureCompiler.php +++ b/lib/Minify/Minify/ClosureCompiler.php @@ -90,7 +90,14 @@ private static function _getCmd($userOptions, $tmpFile) ), $userOptions ); - $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) + + $javaExecutable = self::$javaExecutable; + + if ( false !== strpos(trim($javaExecutable), ' ') ) { + $javaExecutable = '"'.$javaExecutable.'"'; + } + + $cmd = $javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) ? " --charset {$o['charset']}" : ''); diff --git a/lib/Minify/Minify/HTML.php b/lib/Minify/Minify/HTML.php index a070d03..3315c9a 100644 --- a/lib/Minify/Minify/HTML.php +++ b/lib/Minify/Minify/HTML.php @@ -184,7 +184,7 @@ protected function _commentCB($m) protected function _ignoredComment($comment) { foreach ($this->_ignoredComments as $ignoredComment) { - if (stristr($comment, $ignoredComment) !== false) { + if (!empty($ignoredComment) && stristr($comment, $ignoredComment) !== false) { return true; } } diff --git a/lib/Minify/Minify/YUICompressor.php b/lib/Minify/Minify/YUICompressor.php index 133a37d..6f4a12b 100644 --- a/lib/Minify/Minify/YUICompressor.php +++ b/lib/Minify/Minify/YUICompressor.php @@ -1,17 +1,17 @@ * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar'; * Minify_YUICompressor::$tempDir = '/tmp'; @@ -25,7 +25,7 @@ * array('stack-size' => '2048k') * * @todo unit tests, $options docs - * + * * @package Minify * @author Stephen Clay */ @@ -38,7 +38,7 @@ class Minify_YUICompressor { * @var string */ public static $jarFile = null; - + /** * Writable temp directory. This must be set before calling minifyJs() * or minifyCss(). @@ -46,40 +46,40 @@ class Minify_YUICompressor { * @var string */ public static $tempDir = null; - + /** * Filepath of "java" executable (may be needed if not in shell's PATH) * * @var string */ public static $javaExecutable = 'java'; - + /** * Minify a Javascript string - * + * * @param string $js - * + * * @param array $options (verbose is ignored) - * + * * @see http://www.julienlecomte.net/yuicompressor/README - * - * @return string + * + * @return string */ public static function minifyJs($js, $options = array()) { return self::_minify('js', $js, $options); } - + /** * Minify a CSS string - * + * * @param string $css - * + * * @param array $options (verbose is ignored) - * + * * @see http://www.julienlecomte.net/yuicompressor/README - * - * @return string + * + * @return string */ public static function minifyCss($css, $options = array()) { @@ -88,7 +88,7 @@ public static function minifyCss($css, $options = array()) return $css; } - + private static function _minify($type, $content, $options) { self::_prepare(); @@ -103,7 +103,7 @@ private static function _minify($type, $content, $options) } return implode("\n", $output); } - + private static function _getCmd($userOptions, $type, $tmpFile) { if (!is_file(self::$javaExecutable)) { @@ -125,28 +125,34 @@ private static function _getCmd($userOptions, $type, $tmpFile) ) ,$userOptions ); - $cmd = self::$javaExecutable + + $javaExecutable = self::$javaExecutable; + if ( false !== strpos(trim($javaExecutable), ' ') ) { + $javaExecutable = '"'. $javaExecutable . '"'; + } + + $cmd = $javaExecutable . (!empty($o['stack-size']) ? ' -Xss' . $o['stack-size'] : '') . ' -jar ' . escapeshellarg(self::$jarFile) . " --type {$type}" . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) - ? " --charset {$o['charset']}" + ? " --charset {$o['charset']}" : '') . (is_numeric($o['line-break']) && $o['line-break'] >= 0 ? ' --line-break ' . (int)$o['line-break'] : ''); if ($type === 'js') { foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) { - $cmd .= $o[$opt] + $cmd .= $o[$opt] ? " --{$opt}" : ''; } } return $cmd . ' ' . escapeshellarg($tmpFile); } - + private static function _prepare() { if (! is_file(self::$jarFile)) { diff --git a/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php new file mode 100644 index 0000000..d941337 --- /dev/null +++ b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php @@ -0,0 +1,155 @@ + 'azure', + '#f5f5dc' => 'beige', + '#ffe4c4' => 'bisque', + '#a52a2a' => 'brown', + '#ff7f50' => 'coral', + '#ffd700' => 'gold', + '#808080' => 'gray', + '#008000' => 'green', + '#4b0082' => 'indigo', + '#fffff0' => 'ivory', + '#f0e68c' => 'khaki', + '#faf0e6' => 'linen', + '#800000' => 'maroon', + '#000080' => 'navy', + '#fdf5e6' => 'oldlace', + '#808000' => 'olive', + '#ffa500' => 'orange', + '#da70d6' => 'orchid', + '#cd853f' => 'peru', + '#ffc0cb' => 'pink', + '#dda0dd' => 'plum', + '#800080' => 'purple', + '#f00' => 'red', + '#fa8072' => 'salmon', + '#a0522d' => 'sienna', + '#c0c0c0' => 'silver', + '#fffafa' => 'snow', + '#d2b48c' => 'tan', + '#008080' => 'teal', + '#ff6347' => 'tomato', + '#ee82ee' => 'violet', + '#f5deb3' => 'wheat' + ); + } + + public static function getNamedToHexMap() + { + // Named colors longer than hex counterpart + return array( + 'aliceblue' => '#f0f8ff', + 'antiquewhite' => '#faebd7', + 'aquamarine' => '#7fffd4', + 'black' => '#000', + 'blanchedalmond' => '#ffebcd', + 'blueviolet' => '#8a2be2', + 'burlywood' => '#deb887', + 'cadetblue' => '#5f9ea0', + 'chartreuse' => '#7fff00', + 'chocolate' => '#d2691e', + 'cornflowerblue' => '#6495ed', + 'cornsilk' => '#fff8dc', + 'darkblue' => '#00008b', + 'darkcyan' => '#008b8b', + 'darkgoldenrod' => '#b8860b', + 'darkgray' => '#a9a9a9', + 'darkgreen' => '#006400', + 'darkgrey' => '#a9a9a9', + 'darkkhaki' => '#bdb76b', + 'darkmagenta' => '#8b008b', + 'darkolivegreen' => '#556b2f', + 'darkorange' => '#ff8c00', + 'darkorchid' => '#9932cc', + 'darksalmon' => '#e9967a', + 'darkseagreen' => '#8fbc8f', + 'darkslateblue' => '#483d8b', + 'darkslategray' => '#2f4f4f', + 'darkslategrey' => '#2f4f4f', + 'darkturquoise' => '#00ced1', + 'darkviolet' => '#9400d3', + 'deeppink' => '#ff1493', + 'deepskyblue' => '#00bfff', + 'dodgerblue' => '#1e90ff', + 'firebrick' => '#b22222', + 'floralwhite' => '#fffaf0', + 'forestgreen' => '#228b22', + 'fuchsia' => '#f0f', + 'gainsboro' => '#dcdcdc', + 'ghostwhite' => '#f8f8ff', + 'goldenrod' => '#daa520', + 'greenyellow' => '#adff2f', + 'honeydew' => '#f0fff0', + 'indianred' => '#cd5c5c', + 'lavender' => '#e6e6fa', + 'lavenderblush' => '#fff0f5', + 'lawngreen' => '#7cfc00', + 'lemonchiffon' => '#fffacd', + 'lightblue' => '#add8e6', + 'lightcoral' => '#f08080', + 'lightcyan' => '#e0ffff', + 'lightgoldenrodyellow' => '#fafad2', + 'lightgray' => '#d3d3d3', + 'lightgreen' => '#90ee90', + 'lightgrey' => '#d3d3d3', + 'lightpink' => '#ffb6c1', + 'lightsalmon' => '#ffa07a', + 'lightseagreen' => '#20b2aa', + 'lightskyblue' => '#87cefa', + 'lightslategray' => '#778899', + 'lightslategrey' => '#778899', + 'lightsteelblue' => '#b0c4de', + 'lightyellow' => '#ffffe0', + 'limegreen' => '#32cd32', + 'mediumaquamarine' => '#66cdaa', + 'mediumblue' => '#0000cd', + 'mediumorchid' => '#ba55d3', + 'mediumpurple' => '#9370db', + 'mediumseagreen' => '#3cb371', + 'mediumslateblue' => '#7b68ee', + 'mediumspringgreen' => '#00fa9a', + 'mediumturquoise' => '#48d1cc', + 'mediumvioletred' => '#c71585', + 'midnightblue' => '#191970', + 'mintcream' => '#f5fffa', + 'mistyrose' => '#ffe4e1', + 'moccasin' => '#ffe4b5', + 'navajowhite' => '#ffdead', + 'olivedrab' => '#6b8e23', + 'orangered' => '#ff4500', + 'palegoldenrod' => '#eee8aa', + 'palegreen' => '#98fb98', + 'paleturquoise' => '#afeeee', + 'palevioletred' => '#db7093', + 'papayawhip' => '#ffefd5', + 'peachpuff' => '#ffdab9', + 'powderblue' => '#b0e0e6', + 'rebeccapurple' => '#663399', + 'rosybrown' => '#bc8f8f', + 'royalblue' => '#4169e1', + 'saddlebrown' => '#8b4513', + 'sandybrown' => '#f4a460', + 'seagreen' => '#2e8b57', + 'seashell' => '#fff5ee', + 'slateblue' => '#6a5acd', + 'slategray' => '#708090', + 'slategrey' => '#708090', + 'springgreen' => '#00ff7f', + 'steelblue' => '#4682b4', + 'turquoise' => '#40e0d0', + 'white' => '#fff', + 'whitesmoke' => '#f5f5f5', + 'yellow' => '#ff0', + 'yellowgreen' => '#9acd32' + ); + } +} diff --git a/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php new file mode 100644 index 0000000..7f639e2 --- /dev/null +++ b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php @@ -0,0 +1,223 @@ +run(); + } + + public function run() + { + $opts = getopt( + 'hi:o:', + array( + 'help', + 'input:', + 'output:', + 'dry-run', + 'keep-sourcemap', + 'keep-sourcemap-comment', + 'linebreak-position:', + 'memory-limit:', + 'pcre-backtrack-limit:', + 'pcre-recursion-limit:', + 'remove-important-comments' + ) + ); + + $help = $this->getOpt(array('h', 'help'), $opts); + $input = $this->getOpt(array('i', 'input'), $opts); + $output = $this->getOpt(array('o', 'output'), $opts); + $dryrun = $this->getOpt('dry-run', $opts); + $keepSourceMapComment = $this->getOpt(array('keep-sourcemap', 'keep-sourcemap-comment'), $opts); + $linebreakPosition = $this->getOpt('linebreak-position', $opts); + $memoryLimit = $this->getOpt('memory-limit', $opts); + $backtrackLimit = $this->getOpt('pcre-backtrack-limit', $opts); + $recursionLimit = $this->getOpt('pcre-recursion-limit', $opts); + $removeImportantComments = $this->getOpt('remove-important-comments', $opts); + + if (!is_null($help)) { + $this->showHelp(); + die(self::SUCCESS_EXIT); + } + + if (is_null($input)) { + fwrite(STDERR, '-i argument is missing' . PHP_EOL); + $this->showHelp(); + die(self::FAILURE_EXIT); + } + + if (!is_readable($input)) { + fwrite(STDERR, 'Input file is not readable' . PHP_EOL); + die(self::FAILURE_EXIT); + } + + $css = file_get_contents($input); + + if ($css === false) { + fwrite(STDERR, 'Input CSS code could not be retrieved from input file' . PHP_EOL); + die(self::FAILURE_EXIT); + } + + $this->setStat('original-size', strlen($css)); + + $cssmin = new Minifier; + + if (!is_null($keepSourceMapComment)) { + $cssmin->keepSourceMapComment(); + } + + if (!is_null($removeImportantComments)) { + $cssmin->removeImportantComments(); + } + + if (!is_null($linebreakPosition)) { + $cssmin->setLineBreakPosition($linebreakPosition); + } + + if (!is_null($memoryLimit)) { + $cssmin->setMemoryLimit($memoryLimit); + } + + if (!is_null($backtrackLimit)) { + $cssmin->setPcreBacktrackLimit($backtrackLimit); + } + + if (!is_null($recursionLimit)) { + $cssmin->setPcreRecursionLimit($recursionLimit); + } + + $this->setStat('compression-time-start', microtime(true)); + + $css = $cssmin->run($css); + + $this->setStat('compression-time-end', microtime(true)); + $this->setStat('peak-memory-usage', memory_get_peak_usage(true)); + $this->setStat('compressed-size', strlen($css)); + + if (!is_null($dryrun)) { + $this->showStats(); + die(self::SUCCESS_EXIT); + } + + if (is_null($output)) { + fwrite(STDOUT, $css . PHP_EOL); + $this->showStats(); + die(self::SUCCESS_EXIT); + } + + if (!is_writable(dirname($output))) { + fwrite(STDERR, 'Output file is not writable' . PHP_EOL); + die(self::FAILURE_EXIT); + } + + if (file_put_contents($output, $css) === false) { + fwrite(STDERR, 'Compressed CSS code could not be saved to output file' . PHP_EOL); + die(self::FAILURE_EXIT); + } + + $this->showStats(); + + die(self::SUCCESS_EXIT); + } + + protected function getOpt($opts, $options) + { + $value = null; + + if (is_string($opts)) { + $opts = array($opts); + } + + foreach ($opts as $opt) { + if (array_key_exists($opt, $options)) { + $value = $options[$opt]; + break; + } + } + + return $value; + } + + protected function setStat($statName, $statValue) + { + $this->stats[$statName] = $statValue; + } + + protected function formatBytes($size, $precision = 2) + { + $base = log($size, 1024); + $suffixes = array('B', 'K', 'M', 'G', 'T'); + return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)]; + } + + protected function formatMicroSeconds($microSecs, $precision = 2) + { + // ms + $time = round($microSecs * 1000, $precision); + + if ($time >= 60 * 1000) { + $time = round($time / 60 * 1000, $precision) .' m'; // m + } elseif ($time >= 1000) { + $time = round($time / 1000, $precision) .' s'; // s + } else { + $time .= ' ms'; + } + + return $time; + } + + protected function showStats() + { + $spaceSavings = round((1 - ($this->stats['compressed-size'] / $this->stats['original-size'])) * 100, 2); + $compressionRatio = round($this->stats['original-size'] / $this->stats['compressed-size'], 2); + $compressionTime = $this->formatMicroSeconds( + $this->stats['compression-time-end'] - $this->stats['compression-time-start'] + ); + $peakMemoryUsage = $this->formatBytes($this->stats['peak-memory-usage']); + + print << [-o ] + + -i|--input File containing uncompressed CSS code. + -o|--output File to use to save compressed CSS code. + +Options: + + -h|--help Prints this usage information. + --dry-run Performs a dry run displaying statistics. + --keep-sourcemap[-comment] Keeps the sourcemap special comment in the output. + --linebreak-position Splits long lines after a specific column in the output. + --memory-limit Sets the memory limit for this script. + --pcre-backtrack-limit Sets the PCRE backtrack limit for this script. + --pcre-recursion-limit Sets the PCRE recursion limit for this script. + --remove-important-comments Removes !important comments from output. + +EOT; + } +} diff --git a/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php new file mode 100644 index 0000000..ff910ed --- /dev/null +++ b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php @@ -0,0 +1,862 @@ +raisePhpLimits = (bool) $raisePhpLimits; + $this->memoryLimit = 128 * 1048576; // 128MB in bytes + $this->pcreBacktrackLimit = 1000 * 1000; + $this->pcreRecursionLimit = 500 * 1000; + $this->hexToNamedColorsMap = Colors::getHexToNamedMap(); + $this->namedToHexColorsMap = Colors::getNamedToHexMap(); + $this->namedToHexColorsRegex = sprintf( + '/([:,( ])(%s)( |,|\)|;|$)/Si', + implode('|', array_keys($this->namedToHexColorsMap)) + ); + $this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex); + $this->setShortenZeroValuesRegexes(); + } + + /** + * Parses & minifies the given input CSS string + * @param string $css + * @return string + */ + public function run($css = '') + { + if (empty($css) || !is_string($css)) { + return ''; + } + + $this->resetRunProperties(); + + if ($this->raisePhpLimits) { + $this->doRaisePhpLimits(); + } + + return $this->minify($css); + } + + /** + * Sets whether to keep or remove sourcemap special comment. + * Sourcemap comments are removed by default. + * @param bool $keepSourceMapComment + */ + public function keepSourceMapComment($keepSourceMapComment = true) + { + $this->keepSourceMapComment = (bool) $keepSourceMapComment; + } + + /** + * Sets whether to keep or remove important comments. + * Important comments outside of a declaration block are kept by default. + * @param bool $removeImportantComments + */ + public function removeImportantComments($removeImportantComments = true) + { + $this->keepImportantComments = !(bool) $removeImportantComments; + } + + /** + * Sets the approximate column after which long lines will be splitted in the output + * with a linebreak. + * @param int $position + */ + public function setLineBreakPosition($position) + { + $this->linebreakPosition = (int) $position; + } + + /** + * Sets the memory limit for this script + * @param int|string $limit + */ + public function setMemoryLimit($limit) + { + $this->memoryLimit = Utils::normalizeInt($limit); + } + + /** + * Sets the maximum execution time for this script + * @param int|string $seconds + */ + public function setMaxExecutionTime($seconds) + { + $this->maxExecutionTime = (int) $seconds; + } + + /** + * Sets the PCRE backtrack limit for this script + * @param int $limit + */ + public function setPcreBacktrackLimit($limit) + { + $this->pcreBacktrackLimit = (int) $limit; + } + + /** + * Sets the PCRE recursion limit for this script + * @param int $limit + */ + public function setPcreRecursionLimit($limit) + { + $this->pcreRecursionLimit = (int) $limit; + } + + /** + * Builds regular expressions needed for shortening zero values + */ + private function setShortenZeroValuesRegexes() + { + $zeroRegex = '0'. $this->unitsGroupRegex; + $numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) '; + $oneZeroSafeProperties = array( + '(?:line-)?height', + '(?:(?:min|max)-)?width', + 'top', + 'left', + 'background-position', + 'bottom', + 'right', + 'border(?:-(?:top|left|bottom|right))?(?:-width)?', + 'border-(?:(?:top|bottom)-(?:left|right)-)?radius', + 'column-(?:gap|width)', + 'margin(?:-(?:top|left|bottom|right))?', + 'outline-width', + 'padding(?:-(?:top|left|bottom|right))?' + ); + + // First zero regex + $regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si'; + $this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex); + + // Multiple zeroes regexes + $regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si'; + $this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex); + $this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex); + $this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex); + } + + /** + * Resets properties whose value may change between runs + */ + private function resetRunProperties() + { + $this->comments = array(); + $this->ruleBodies = array(); + $this->preservedTokens = array(); + } + + /** + * Tries to configure PHP to use at least the suggested minimum settings + * @return void + */ + private function doRaisePhpLimits() + { + $phpLimits = array( + 'memory_limit' => $this->memoryLimit, + 'max_execution_time' => $this->maxExecutionTime, + 'pcre.backtrack_limit' => $this->pcreBacktrackLimit, + 'pcre.recursion_limit' => $this->pcreRecursionLimit + ); + + // If current settings are higher respect them. + foreach ($phpLimits as $name => $suggested) { + $current = Utils::normalizeInt(ini_get($name)); + + if ($current >= $suggested) { + continue; + } + + // memoryLimit exception: allow -1 for "no memory limit". + if ($name === 'memory_limit' && $current === -1) { + continue; + } + + // maxExecutionTime exception: allow 0 for "no memory limit". + if ($name === 'max_execution_time' && $current === 0) { + continue; + } + + ini_set($name, $suggested); + } + } + + /** + * Registers a preserved token + * @param string $token + * @return string The token ID string + */ + private function registerPreservedToken($token) + { + $tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens)); + $this->preservedTokens[$tokenId] = $token; + return $tokenId; + } + + /** + * Registers a candidate comment token + * @param string $comment + * @return string The comment token ID string + */ + private function registerCommentToken($comment) + { + $tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments)); + $this->comments[$tokenId] = $comment; + return $tokenId; + } + + /** + * Registers a rule body token + * @param string $body the minified rule body + * @return string The rule body token ID string + */ + private function registerRuleBodyToken($body) + { + if (empty($body)) { + return ''; + } + + $tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies)); + $this->ruleBodies[$tokenId] = $body; + return $tokenId; + } + + /** + * Parses & minifies the given input CSS string + * @param string $css + * @return string + */ + private function minify($css) + { + // Process data urls + $css = $this->processDataUrls($css); + + // Process comments + $css = preg_replace_callback( + '/(?processComments($css); + + // Process rule bodies + $css = $this->processRuleBodies($css); + + // Process at-rules and selectors + $css = $this->processAtRulesAndSelectors($css); + + // Restore preserved rule bodies before splitting + $css = strtr($css, $this->ruleBodies); + + // Some source control tools don't like it when files containing lines longer + // than, say 8000 characters, are checked in. The linebreak option is used in + // that case to split long lines after a specific column. + if ($this->linebreakPosition > 0) { + $l = strlen($css); + $offset = $this->linebreakPosition; + while (preg_match('/(?linebreakPosition; + $l += 1; + if ($offset > $l) { + break; + } + } + } + + // Restore preserved comments and strings + $css = strtr($css, $this->preservedTokens); + + return trim($css); + } + + /** + * Searches & replaces all data urls with tokens before we start compressing, + * to avoid performance issues running some of the subsequent regexes against large string chunks. + * @param string $css + * @return string + */ + private function processDataUrls($css) + { + $ret = ''; + $searchOffset = $substrOffset = 0; + + // Since we need to account for non-base64 data urls, we need to handle + // ' and ) being part of the data string. + while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) { + $matchStartIndex = $m[0][1]; + $dataStartIndex = $matchStartIndex + 4; // url( length + $searchOffset = $matchStartIndex + strlen($m[0][0]); + $terminator = $m[1][0]; // ', " or empty (not quoted) + $terminatorRegex = '/(?registerPreservedToken(trim($token)) .')'; + // No end terminator found, re-add the whole match. Should we throw/warn here? + } else { + $ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex); + } + + $substrOffset = $searchOffset; + } + + $ret .= substr($css, $substrOffset); + + return $ret; + } + + /** + * Registers all comments found as candidates to be preserved. + * @param array $matches + * @return string + */ + private function processCommentsCallback($matches) + { + return '/*'. $this->registerCommentToken($matches[1]) .'*/'; + } + + /** + * Preserves old IE Matrix string definition + * @param array $matches + * @return string + */ + private function processOldIeSpecificMatrixDefinitionCallback($matches) + { + return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')'; + } + + /** + * Preserves strings found + * @param array $matches + * @return string + */ + private function processStringsCallback($matches) + { + $match = $matches[0]; + $quote = substr($match, 0, 1); + $match = substr($match, 1, -1); + + // maybe the string contains a comment-like substring? + // one, maybe more? put'em back then + if (strpos($match, self::COMMENT_TOKEN_START) !== false) { + $match = strtr($match, $this->comments); + } + + // minify alpha opacity in filter strings + $match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match); + + return $quote . $this->registerPreservedToken($match) . $quote; + } + + /** + * Preserves or removes comments found. + * @param string $css + * @return string + */ + private function processComments($css) + { + foreach ($this->comments as $commentId => $comment) { + $commentIdString = '/*'. $commentId .'*/'; + + // ! in the first position of the comment means preserve + // so push to the preserved tokens keeping the ! + if ($this->keepImportantComments && strpos($comment, '!') === 0) { + $preservedTokenId = $this->registerPreservedToken($comment); + // Put new lines before and after /*! important comments + $css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css); + continue; + } + + // # sourceMappingURL= in the first position of the comment means sourcemap + // so push to the preserved tokens if {$this->keepSourceMapComment} is truthy. + if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) { + $preservedTokenId = $this->registerPreservedToken($comment); + // Add new line before the sourcemap comment + $css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css); + continue; + } + + // Keep empty comments after child selectors (IE7 hack) + // e.g. html >/**/ body + if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) { + $css = str_replace($commentId, $this->registerPreservedToken(''), $css); + continue; + } + + // in all other cases kill the comment + $css = str_replace($commentIdString, '', $css); + } + + // Normalize whitespace again + $css = preg_replace('/ +/S', ' ', $css); + + return $css; + } + + /** + * Finds, minifies & preserves all rule bodies. + * @param string $css the whole stylesheet. + * @return string + */ + private function processRuleBodies($css) + { + $ret = ''; + $searchOffset = $substrOffset = 0; + + while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) { + $blockEndPos = strpos($css, '}', $blockStartPos); + $nextBlockStartPos = strpos($css, '{', $blockStartPos + 1); + $ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset); + + if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) { + $ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos); + $searchOffset = $nextBlockStartPos; + } else { + $ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1); + $ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody)); + $ret .= '{'. $ruleBodyToken .'}'; + $searchOffset = $blockEndPos + 1; + } + + $substrOffset = $searchOffset; + } + + $ret .= substr($css, $substrOffset); + + return $ret; + } + + /** + * Compresses non-group rule bodies. + * @param string $body The rule body without curly braces + * @return string + */ + private function processRuleBody($body) + { + $body = trim($body); + + // Remove spaces before the things that should not have spaces before them. + $body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body); + + // Remove the spaces after the things that should not have spaces after them. + $body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body); + + // Replace multiple semi-colons in a row by a single one + $body = preg_replace('/;;+/S', ';', $body); + + // Remove semicolon before closing brace except when: + // - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers. + if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) { + $body = rtrim($body, ';'); + } + + // Remove important comments inside a rule body (because they make no sense here). + if (strpos($body, '/*') !== false) { + $body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body); + } + + // Empty rule body? Exit :) + if (empty($body)) { + return ''; + } + + // Shorten font-weight values + $body = preg_replace( + array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'), + array('${1}700', '${1}400'), + $body + ); + + // Shorten background property + $body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body); + + // Shorten opacity IE filter + $body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body); + + // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space) + // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space) + // This makes it more likely that it'll get further compressed in the next step. + $body = preg_replace_callback( + '/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si', + array($this, 'shortenHslAndRgbToHexCallback'), + $body + ); + + // Shorten colors from #AABBCC to #ABC or shorter color name: + // - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters) + $body = preg_replace_callback( + '/(? #fff. + // Run at least 2 times to cover most cases + $body = preg_replace_callback( + array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex), + array($this, 'shortenNamedColorsCallback'), + $body + ); + + // Replace positive sign from numbers before the leading space is removed. + // +1.2em to 1.2em, +.8px to .8px, +2% to 2% + $body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body); + + // shorten ms to s + $body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) { + return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s'; + }, $body); + + // Remove leading zeros from integer and float numbers. + // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05 + $body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body); + + // Remove trailing zeros from float numbers. + // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px + $body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body); + + // Remove trailing .0 -> -9.0 to -9 + $body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body); + + // Replace 0 length numbers with 0 + $body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body); + + // Shorten zero values for safe properties only + $body = preg_replace( + array( + $this->shortenOneZeroesRegex, + $this->shortenTwoZeroesRegex, + $this->shortenThreeZeroesRegex, + $this->shortenFourZeroesRegex + ), + array( + '$1$2:0', + '$1$2:$3 0', + '$1$2:$3 $4 0', + '$1$2:$3 $4 $5 0' + ), + $body + ); + + // Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property. + $body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body); + + // Shorten suitable shorthand properties with repeated values + $body = preg_replace( + array( + '/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si', + '/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si' + ), + '$1:$2$3', + $body + ); + $body = preg_replace( + array( + '/(margin|padding|border-(?:width|radius)):'. + '('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si', + '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si' + ), + '$1:$2 $3$4', + $body + ); + $body = preg_replace( + array( + '/(margin|padding|border-(?:width|radius)):'. + '('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si', + '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si' + ), + '$1:$2 $3 $4$5', + $body + ); + + // Lowercase some common functions that can be values + $body = preg_replace_callback( + '/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'. + 'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'. + 'steps|to|url|var|-webkit-gradient|'. + '(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si', + array($this, 'strtolowerCallback'), + $body + ); + + // Lowercase all uppercase properties + $body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body); + + return $body; + } + + /** + * Compresses At-rules and selectors. + * @param string $css the whole stylesheet with rule bodies tokenized. + * @return string + */ + private function processAtRulesAndSelectors($css) + { + $charset = ''; + $imports = ''; + $namespaces = ''; + + // Remove spaces before the things that should not have spaces before them. + $css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css); + + // Remove the spaces after the things that should not have spaces after them. + $css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css); + + // Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2) + $css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css); + + // Retain space for special IE6 cases + $css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) { + return ':first-'. strtolower($matches[1]) .' '. $matches[2]; + }, $css); + + // Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1) + // Add token to add the "/" back in later + $css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css); + + // Remove empty rule blocks up to 2 levels deep. + $css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css); + $css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css); + + // Two important comments next to each other? Remove extra newline. + if ($this->keepImportantComments) { + $css = str_replace("\n\n", "\n", $css); + } + + // Restore fraction + $css = str_replace(self::QUERY_FRACTION, '/', $css); + + // Lowercase some popular @directives + $css = preg_replace_callback( + '/(?charsetRegex, $css, $matches)) { + // Keep the first @charset at-rule found + $charset = $matches[0]; + // Delete all @charset at-rules + $css = preg_replace($this->charsetRegex, '', $css); + } + + // @import handling + $css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) { + // Keep all @import at-rules found for later + $imports .= $matches[0]; + // Delete all @import at-rules + return ''; + }, $css); + + // @namespace handling + $css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) { + // Keep all @namespace at-rules found for later + $namespaces .= $matches[0]; + // Delete all @namespace at-rules + return ''; + }, $css); + + // Order critical at-rules: + // 1. @charset first + // 2. @imports below @charset + // 3. @namespaces below @imports + $css = $charset . $imports . $namespaces . $css; + + return $css; + } + + /** + * Converts hsl() & rgb() colors to HEX format. + * @param $matches + * @return string + */ + private function shortenHslAndRgbToHexCallback($matches) + { + $type = $matches[1]; + $values = explode(',', $matches[2]); + $terminator = $matches[3]; + + if ($type === 'hsl') { + $values = Utils::hslToRgb($values); + } + + $hexColors = Utils::rgbToHex($values); + + // Restore space after rgb() or hsl() function in some cases such as: + // background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%); + if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) { + $terminator = ' '. $terminator; + } + + return '#'. implode('', $hexColors) . $terminator; + } + + /** + * Compresses HEX color values of the form #AABBCC to #ABC or short color name. + * @param $matches + * @return string + */ + private function shortenHexColorsCallback($matches) + { + $hex = $matches[1]; + + // Shorten suitable 6 chars HEX colors + if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) { + $hex = $m[1] . $m[2] . $m[3]; + } + + // Lowercase + $hex = '#'. strtolower($hex); + + // Replace Hex colors with shorter color names + $color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex; + + return $color . $matches[2]; + } + + /** + * Shortens all named colors with a shorter HEX counterpart for a set of safe properties + * e.g. white -> #fff + * @param array $matches + * @return string + */ + private function shortenNamedColorsCallback($matches) + { + return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3]; + } + + /** + * Makes a string lowercase + * @param array $matches + * @return string + */ + private function strtolowerCallback($matches) + { + return strtolower($matches[0]); + } +} diff --git a/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php new file mode 100644 index 0000000..64bce85 --- /dev/null +++ b/lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php @@ -0,0 +1,149 @@ + 1 ? $vh - 1 : $vh); + + if ($vh * 6 < 1) { + return $v1 + ($v2 - $v1) * 6 * $vh; + } + + if ($vh * 2 < 1) { + return $v2; + } + + if ($vh * 3 < 2) { + return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6; + } + + return $v1; + } + + /** + * Convert strings like "64M" or "30" to int values + * @param mixed $size + * @return int + */ + public static function normalizeInt($size) + { + if (is_string($size)) { + $letter = substr($size, -1); + $size = intval($size); + switch ($letter) { + case 'M': + case 'm': + return (int) $size * 1048576; + case 'K': + case 'k': + return (int) $size * 1024; + case 'G': + case 'g': + return (int) $size * 1073741824; + } + } + return (int) $size; + } + + /** + * Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5 + * @param $rgbPercentage + * @return int + */ + public static function rgbPercentageToRgbInteger($rgbPercentage) + { + if (strpos($rgbPercentage, '%') !== false) { + $rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55); + } + + return intval($rgbPercentage, 10); + } + + /** + * Converts a RGB color into a HEX color + * @param array $rgbColors + * @return array + */ + public static function rgbToHex($rgbColors) + { + $hexColors = array(); + + // Values outside the sRGB color space should be clipped (0-255) + for ($i = 0, $l = count($rgbColors); $i < $l; $i++) { + $hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i]))); + } + + return $hexColors; + } + + /** + * Rounds a number to its closest integer + * @param $n + * @return int + */ + public static function roundNumber($n) + { + return intval(round(floatval($n)), 10); + } +} diff --git a/lib/NetDNA/NetDNA.php b/lib/NetDNA/NetDNA.php index b563521..6d0408d 100644 --- a/lib/NetDNA/NetDNA.php +++ b/lib/NetDNA/NetDNA.php @@ -1,7 +1,7 @@ alias = $alias; $this->key = $key; $this->secret = $secret; } - public function get_zone_domain($name) { - return $name . '.' . $this->alias . '.netdna-cdn.com'; - } - - public function is_valid() { - return !empty($this->alias) && !empty($this->key) && - !empty($this->secret); - } - - /** - * @param $selected_call - * @param $method_type - * @param $params - * @return string - * @throws W3tcWpHttpException - */ - private function execute($selected_call, $method_type, $params) { - //increase the http request timeout - add_filter('http_request_timeout', array($this, 'filter_timeout_time')); - add_filter('https_ssl_verify', array($this, 'https_ssl_verify')); - - $consumer = new W3tcOAuthConsumer($this->key, $this->secret, NULL); + public function get_zone_domain($name) { + return $name . '.' . $this->alias . '.netdna-cdn.com'; + } + + public function is_valid() { + return !empty($this->alias) && !empty($this->key) && + !empty($this->secret); + } + + /** + * @param $selected_call + * @param $method_type + * @param $params + * @return string + * @throws W3tcWpHttpException + */ + private function execute($selected_call, $method_type, $params) { + //increase the http request timeout + add_filter('http_request_timeout', array($this, 'filter_timeout_time')); + add_filter('https_ssl_verify', array($this, 'https_ssl_verify')); + + $consumer = new W3tcOAuthConsumer($this->key, $this->secret, NULL); // the endpoint for your request $endpoint = "$this->netdnarws_url/$this->alias$selected_call"; @@ -78,7 +78,7 @@ private function execute($selected_call, $method_type, $params) { //parse endpoint before creating OAuth request $parsed = parse_url($endpoint); if (array_key_exists("parsed", $parsed)) { - parse_str($parsed['query'], $params); + parse_str($parsed['query'], $params); } //generate a request from your consumer @@ -88,37 +88,37 @@ private function execute($selected_call, $method_type, $params) { $sig_method = new W3tcOAuthSignatureMethod_HMAC_SHA1(); $req_req->sign_request($sig_method, $consumer, NULL); - $request = array(); - $request['sslverify'] = false; - $request['method'] = $method_type; + $request = array(); + $request['sslverify'] = false; + $request['method'] = $method_type; - if ($method_type == "POST") { - $request['body'] = $req_req->to_postdata(); - $request['headers']['Content-Type'] = - 'application/x-www-form-urlencoded; charset=' . get_option('blog_charset'); + if ($method_type == "POST" || $method_type == "PUT") { + $request['body'] = $req_req->to_postdata(); + $request['headers']['Content-Type'] = + 'application/x-www-form-urlencoded; charset=' . get_option('blog_charset'); - $url = $req_req->get_normalized_http_url(); - } else { - // notice GET, PUT and DELETE both needs to be passed in URL - $url = $req_req->to_url(); - } + $url = $req_req->get_normalized_http_url(); + } else { + // notice GET, PUT and DELETE both needs to be passed in URL + $url = $req_req->to_url(); + } - $response = wp_remote_request($url, $request); + $response = wp_remote_request($url, $request); - $json_output = ''; - if (!is_wp_error($response)) { + $json_output = ''; + if (!is_wp_error($response)) { // make call - $result = wp_remote_retrieve_body($response); - $headers = wp_remote_retrieve_headers($response); - $response_code = wp_remote_retrieve_response_code($response); - // $json_output contains the output string - $json_output = $result; - } else { - $response_code = $response->get_error_code(); - } - - remove_filter('https_ssl_verify', array($this, 'https_ssl_verify')); - remove_filter('http_request_timeout', array($this, 'filter_timeout_time')); + $result = wp_remote_retrieve_body($response); + $headers = wp_remote_retrieve_headers($response); + $response_code = wp_remote_retrieve_response_code($response); + // $json_output contains the output string + $json_output = $result; + } else { + $response_code = $response->get_error_code(); + } + + remove_filter('https_ssl_verify', array($this, 'https_ssl_verify')); + remove_filter('http_request_timeout', array($this, 'filter_timeout_time')); // catch errors if(is_wp_error($response)) { @@ -128,358 +128,375 @@ private function execute($selected_call, $method_type, $params) { return $json_output; } - /** - * @param $selected_call - * @param array $params - * @return string - * @throws W3tcWpHttpException - */ - public function get($selected_call, $params = array()){ + /** + * @param $selected_call + * @param array $params + * @return string + * @throws W3tcWpHttpException + */ + public function get($selected_call, $params = array()){ return $this->execute($selected_call, 'GET', $params); } - /** - * @param $selected_call - * @param array $params - * @return string - * @throws W3tcWpHttpException - */ - public function post($selected_call, $params = array()){ + /** + * @param $selected_call + * @param array $params + * @return string + * @throws W3tcWpHttpException + */ + public function post($selected_call, $params = array()){ return $this->execute($selected_call, 'POST', $params); } - /** - * @param $selected_call - * @param array $params - * @return string - * @throws W3tcWpHttpException - */ - public function put($selected_call, $params = array()){ + /** + * @param $selected_call + * @param array $params + * @return string + * @throws W3tcWpHttpException + */ + public function put($selected_call, $params = array()){ return $this->execute($selected_call, 'PUT', $params); } - /** - * @param $selected_call - * @param array $params - * @return string - * @throws W3tcWpHttpException - */ - public function delete($selected_call, $params = array()){ + /** + * @param $selected_call + * @param array $params + * @return string + * @throws W3tcWpHttpException + */ + public function delete($selected_call, $params = array()){ return $this->execute($selected_call, 'DELETE', $params); } - /** - * Finds the zone id that matches the provided url. - * @param $url - * @return null|int - * @throws W3tcWpHttpException - */ - public function get_zone_id($url) { - $zone_id = null; - $pull_zones = json_decode($this->get('/zones/pull.json')); - - if (preg_match("(200|201)", $pull_zones->code)) { - foreach ($pull_zones->data->pullzones as $zone) { - if (trim($zone->url, '/') != trim($url, '/')) - continue; - else { - $zone_id = $zone->id; - break; - } - } - } else - return null; - return $zone_id; - } - - /** - * Retrieves statistics for the zone id - * @param $zone_id - * @return null|array - * @throws W3tcWpHttpException - */ - public function get_stats_per_zone($zone_id) { - $api_stats = json_decode($this->get("/reports/{$zone_id}/stats.json"), true); - if (preg_match("(200|201)", $api_stats['code'])) { - $summary = $api_stats['data']['summary']; - return $summary; - } else - return null; - } - - /** - * Returns list of files for the zone id - * @param $zone_id - * @return null|array - * @throws W3tcWpHttpException - */ - public function get_list_of_file_types_per_zone($zone_id) { - $api_list = json_decode($this->get("/reports/pull/{$zone_id}/filetypes.json"), true); - if (preg_match("(200|201)", $api_list['code'])) { - $stats['total'] = $api_list['data']['total']; - - foreach($api_list['data']['filetypes'] as $filetyp) { - $stats['filetypes'][] = $filetyp; - } - $stats['summary'] = $api_list['data']['summary']; - return $stats; - } else - return null; - } - - /** - * Retrieves a list of popular files for zone id - * - * @param $zone_id - * @return null|array - * @throws W3tcWpHttpException - */ - public function get_list_of_popularfiles_per_zone($zone_id) { - $api_popularfiles = json_decode($this->get("/reports/{$zone_id}/popularfiles.json"), true); - if (preg_match("(200|201)", $api_popularfiles['code'])) { - $popularfiles = $api_popularfiles['data']['popularfiles']; - return $popularfiles; - } else - return null; - } - - /** - * Retrieves an account connected with the authorization key - * - * @throws Exception - * @return null|string - */ - public function get_account() { - $api_account = json_decode($this->get("/account.json"), true); - if (preg_match("(200|201)", $api_account['code'])) { - $account = $api_account['data']['account']; - return $account; - } else - throw new Exception($api_account['error']['message']); - } - - /** - * Retrieves a pull zone - * @param $zone_id - * @throws Exception - * @return null|string - */ - public function get_pull_zone($zone_id) { - $api_pull_zone = json_decode($this->get("/zones/pull.json/{$zone_id}"), true); - if (preg_match("(200|201)", $api_pull_zone['code'])) { - $pull_zone = $api_pull_zone['data']['pullzone']; - return $pull_zone; - } else - throw new Exception($api_pull_zone['error']['message']); - } - - /** - * Creates a pull zone - * @param $zone - * @return mixed - * @throws Exception - */ - public function create_pull_zone($zone) { - $zone_data = json_decode($this->post('/zones/pull.json', $zone), true); - if (preg_match("(200|201)", $zone_data['code'])) { - return $zone_data['data']['pullzone']; - } else - throw new Exception($zone_data['error']['message']); - } - - /** - * Returns all zones connected to an url - * @param $url - * @throws Exception - * @return array|null - */ - public function get_zones_by_url($url) { - $zone_id = null; - $pull_zones = json_decode($this->get('/zones/pull.json'), true); - $zones = array(); - if (preg_match("(200|201)", $pull_zones['code'])) { - foreach ($pull_zones ['data']['pullzones'] as $zone) { - if (trim($zone['url'], '/') != trim($url, '/')) - continue; - else { - $zones[] = $zone; - } - } - } else - throw new Exception($pull_zones['error']['message']); - return $zones; - } - - /** - * Retrieves pull zones - * @throws Exception - * @return array|null - */ - public function get_pull_zones() { - $pull_zones = json_decode($this->get('/zones/pull.json'), true); - $zones = array(); - if (preg_match("(200|201)", $pull_zones['code'])) { - foreach ($pull_zones ['data']['pullzones'] as $zone) { - $zones[] = $zone; - } - } else { - throw new Exception($pull_zones['error']['message']); - } - return $zones; - } - - /** - * Increase http request timeout to 60 seconds - * @param int $time - * @return int - */ - public function filter_timeout_time($time) { - return 60; - } - - /** - * Don't check certificate, some users have limited CA list - */ - public function https_ssl_verify($v) { - return false; - } - - /** - * Update a pull zone - * @param $zone_id - * @param $zone - * @throws Exception - * @return - */ - public function update_pull_zone($zone_id, $zone) { - $zone_data = json_decode($this->put("/zones/pull.json/$zone_id", $zone), true); - if (preg_match("(200|201)", $zone_data['code'])) { - return $zone_data['data']['pullzone']; - } else - throw new Exception($zone_data['error']['message']); - } - - /** - * Creates a new pull zone with default settings - * @param string $url the sites url 4-100 chars; only valid URLs accepted - * @param null|string $name 3-32 chars; only letters, digits, and dash (-)accepted - * @param null|string $label length: 1-255 chars - * @param array $zone_settings custom settings - * @return string - */ - public function create_default_pull_zone($url, $name = null, $label = null, $zone_settings=array()) { - $zone_defaults = array(); - if (is_null($name)) { - $name = md5($url); - $len = strlen($name)>24 ? 24 : strlen($name); - $name = substr($name, 0, $len); - } - if (is_null($label)) - $label = sprintf(__('Zone for %s was created by W3 Total Cache', 'w3-total-cache'), $url); - $zone_defaults['name'] = $name; - $zone_defaults['label'] = $label; - $zone_defaults['url'] = $url; - $zone_defaults['use_stale'] = 0; - $zone_defaults['queries'] = 1; - $zone_defaults['compress'] = 1; - $zone_defaults['backend_compress'] = 1; - $zone_defaults['disallow_robots'] = 1; - $zone_defaults = array_merge( $zone_defaults, $zone_settings); - $response = $this->create_pull_zone($zone_defaults); - return $response; - } - - /** - * Returns number of zones - * @throws Exception - * @return array - */ - public function get_zone_count() { - $pull_zones = json_decode($this->get('/zones.json/count'), true); - if (preg_match("(200|201)", $pull_zones['code'])) { - return intval($pull_zones ['data']['count']); - } else - throw new Exception($pull_zones['error']['message']); - } - - /** - * Creates custom domains - * @param $zone_id - * @throws Exception - * @return array|null - */ - public function create_custom_domain($zone_id, $custom_domain) { - $custom_domain = json_decode($this->post("/zones/pull/$zone_id/customdomains.json", array( - 'custom_domain' => $custom_domain)), true); - if (preg_match("(200|201)", $custom_domain['code'])) { - return $custom_domain; - } else - throw $this->to_exception($custom_domain); - } - - private function to_exception($response) { - $message = $response['error']['message']; - if (isset($response['data']) && isset($response['data']['errors'])) { - foreach ($response['data']['errors'] as $field => $error) - $message .= '. ' . $field . ': ' . $error; - } - - return new Exception($message); - } - - /** - * Returns custom domains - * @param $zone_id - * @throws Exception - * @return array|null - */ - public function get_custom_domains($zone_id) { - $custom_domains = json_decode($this->get("/zones/pull/$zone_id/customdomains.json"), true); - $domains = array(); - if (preg_match("(200|201)", $custom_domains['code'])) { - foreach ($custom_domains['data']['customdomains'] as $domain) { - $domains[] = $domain['custom_domain']; - } - } else - throw new Exception($custom_domains['error']['message']); - return $domains; - } - - /** - * Returns the zone data for the provided zone id - * - * @param int $zone_id - * @throws Exception - * @return array - */ - public function get_zone($zone_id) { - $zone_data = json_decode($this->get("/zones/pull.json/$zone_id"), true); - if (preg_match("(200|201)", $zone_data['code'])) { - return $zone_data['data']['pullzone']; - } else - throw new Exception($zone_data['error']['message']); - } - - /** - * Deletes files from cache - * @param $zone_id - * @param $files array of relative paths to files to delete - * Deletes whole zone if empty list passed - **/ - public function cache_delete($zone_id, $files = array()) { - if (empty($files)) - $params = array(); - else - $params = array('files' => $files); - - $response = json_decode($this->delete( - '/zones/pull.json/' . $zone_id . '/cache', - $params), true); - - if (preg_match("(200|201)", $response['code'])) { - return true; - } else - throw $this->to_exception($response); - } + /** + * Finds the zone id that matches the provided url. + * @param $url + * @return null|int + * @throws W3tcWpHttpException + */ + public function get_zone_id($url) { + $zone_id = null; + $pull_zones = json_decode($this->get('/zones/pull.json')); + + if (preg_match("(200|201)", $pull_zones->code)) { + foreach ($pull_zones->data->pullzones as $zone) { + if (trim($zone->url, '/') != trim($url, '/')) + continue; + else { + $zone_id = $zone->id; + break; + } + } + } else + return null; + return $zone_id; + } + + /** + * Retrieves statistics for the zone id + * @param $zone_id + * @return null|array + * @throws W3tcWpHttpException + */ + public function get_stats_per_zone($zone_id) { + $api_stats = json_decode($this->get("/reports/{$zone_id}/stats.json"), true); + if (preg_match("(200|201)", $api_stats['code'])) { + $summary = $api_stats['data']['summary']; + return $summary; + } else + return null; + } + + /** + * Returns list of files for the zone id + * @param $zone_id + * @return null|array + * @throws W3tcWpHttpException + */ + public function get_list_of_file_types_per_zone($zone_id) { + $api_list = json_decode($this->get("/reports/pull/{$zone_id}/filetypes.json"), true); + if (preg_match("(200|201)", $api_list['code'])) { + $stats['total'] = $api_list['data']['total']; + + foreach($api_list['data']['filetypes'] as $filetyp) { + $stats['filetypes'][] = $filetyp; + } + $stats['summary'] = $api_list['data']['summary']; + return $stats; + } else + return null; + } + + /** + * Retrieves a list of popular files for zone id + * + * @param $zone_id + * @return null|array + * @throws W3tcWpHttpException + */ + public function get_list_of_popularfiles_per_zone($zone_id) { + $api_popularfiles = json_decode($this->get("/reports/{$zone_id}/popularfiles.json"), true); + if (preg_match("(200|201)", $api_popularfiles['code'])) { + $popularfiles = $api_popularfiles['data']['popularfiles']; + return $popularfiles; + } else + return null; + } + + /** + * Retrieves an account connected with the authorization key + * + * @throws Exception + * @return null|string + */ + public function get_account() { + $api_account = json_decode($this->get("/account.json"), true); + if (preg_match("(200|201)", $api_account['code'])) { + $account = $api_account['data']['account']; + return $account; + } else + throw new Exception($this->error_message($api_account)); + } + + /** + * Retrieves a pull zone + * @param $zone_id + * @throws Exception + * @return null|string + */ + public function get_pull_zone($zone_id) { + $api_pull_zone = json_decode($this->get("/zones/pull.json/{$zone_id}"), true); + if (preg_match("(200|201)", $api_pull_zone['code'])) { + $pull_zone = $api_pull_zone['data']['pullzone']; + return $pull_zone; + } else + throw new Exception($this->error_message($api_pull_zone)); + } + + /** + * Creates a pull zone + * @param $zone + * @return mixed + * @throws Exception + */ + public function create_pull_zone($zone) { + $zone_data = json_decode($this->post('/zones/pull.json', $zone), true); + if (preg_match("(200|201)", $zone_data['code'])) { + return $zone_data['data']['pullzone']; + } else + throw new Exception($this->error_message($zone_data)); + } + + private function error_message($o) { + $m = isset( $o['error']['message'] ) ? $o['error']['message'] : ''; + + if ( isset( $o['data']['errors'] ) && is_array( $o['data']['errors'] ) ) { + foreach ( $o['data']['errors'] as $k => $v ) { + $m .= '. ' . $k . ': ' . $v; + } + } + + return $m; + } + + /** + * Returns all zones connected to an url + * @param $url + * @throws Exception + * @return array|null + */ + public function get_zones_by_url($url) { + $zone_id = null; + $pull_zones = json_decode($this->get('/zones/pull.json'), true); + $zones = array(); + if (preg_match("(200|201)", $pull_zones['code'])) { + foreach ($pull_zones ['data']['pullzones'] as $zone) { + if (trim($zone['url'], '/') != trim($url, '/')) + continue; + else { + $zones[] = $zone; + } + } + } else + throw new Exception($this->error_message($pull_zones)); + return $zones; + } + + /** + * Retrieves pull zones + * @throws Exception + * @return array|null + */ + public function get_pull_zones() { + $pull_zones = json_decode($this->get('/zones/pull.json'), true); + $zones = array(); + if (preg_match("(200|201)", $pull_zones['code'])) { + foreach ($pull_zones ['data']['pullzones'] as $zone) { + $zones[] = $zone; + } + } else { + throw new Exception($this->error_message($zone_data)); + } + return $zones; + } + + /** + * Increase http request timeout to 60 seconds + * @param int $time + * @return int + */ + public function filter_timeout_time($time) { + return 600; + } + + /** + * Don't check certificate, some users have limited CA list + */ + public function https_ssl_verify($v) { + return false; + } + + /** + * Update a pull zone + * @param $zone_id + * @param $zone + * @throws Exception + * @return + */ + public function update_pull_zone($zone_id, $zone) { + $zone_data = json_decode($this->put("/zones/pull.json/$zone_id", $zone), true); + if (preg_match("(200|201)", $zone_data['code'])) { + return $zone_data['data']['pullzone']; + } else { + throw new Exception($this->error_message($zone_data)); + } + } + + /** + * Creates a new pull zone with default settings + * @param string $url the sites url 4-100 chars; only valid URLs accepted + * @param null|string $name 3-32 chars; only letters, digits, and dash (-)accepted + * @param null|string $label length: 1-255 chars + * @param array $zone_settings custom settings + * @return string + */ + public function create_default_pull_zone($url, $name = null, $label = null, $zone_settings=array()) { + $zone_defaults = array(); + if (is_null($name)) { + $name = md5($url); + $len = strlen($name)>24 ? 24 : strlen($name); + $name = substr($name, 0, $len); + } + if (is_null($label)) + $label = sprintf(__('Zone for %s was created by W3 Total Cache', 'w3-total-cache'), $url); + $zone_defaults['name'] = $name; + $zone_defaults['label'] = $label; + $zone_defaults['url'] = $url; + $zone_defaults['use_stale'] = 0; + $zone_defaults['queries'] = 1; + $zone_defaults['compress'] = 1; + $zone_defaults['backend_compress'] = 1; + $zone_defaults['disallow_robots'] = 1; + $zone_defaults = array_merge( $zone_defaults, $zone_settings); + $response = $this->create_pull_zone($zone_defaults); + return $response; + } + + /** + * Returns number of zones + * @throws Exception + * @return array + */ + public function get_zone_count() { + $pull_zones = json_decode($this->get('/zones.json/count'), true); + if (preg_match("(200|201)", $pull_zones['code'])) { + return intval($pull_zones ['data']['count']); + } else + throw new Exception($this->error_message($pull_zones)); + } + + /** + * Creates custom domains + * @param $zone_id + * @throws Exception + * @return array|null + */ + public function create_custom_domain($zone_id, $custom_domain) { + $custom_domain = json_decode($this->post("/zones/pull/$zone_id/customdomains.json", array( + 'custom_domain' => $custom_domain)), true); + if (preg_match("(200|201)", $custom_domain['code'])) { + return $custom_domain; + } else + throw $this->to_exception($custom_domain); + } + + private function to_exception($response) { + $message = $response['error']['message']; + if ( isset( $response['data'] ) && isset( $response['data']['errors'] ) ) { + foreach ( $response['data']['errors'] as $field => $error ) { + if ( isset( $error['error'] ) ) + $message .= '. ' . $field . ': ' . $error['error']; + else + $message .= '. ' . $field . ': ' . $error; + } + } + + return new Exception($message); + } + + /** + * Returns custom domains + * @param $zone_id + * @throws Exception + * @return array|null + */ + public function get_custom_domains($zone_id) { + $custom_domains = json_decode($this->get("/zones/pull/$zone_id/customdomains.json"), true); + $domains = array(); + if (preg_match("(200|201)", $custom_domains['code'])) { + foreach ($custom_domains['data']['customdomains'] as $domain) { + $domains[] = $domain['custom_domain']; + } + } else + throw new Exception($this->error_message($custom_domains)); + return $domains; + } + + /** + * Returns the zone data for the provided zone id + * + * @param int $zone_id + * @throws Exception + * @return array + */ + public function get_zone($zone_id) { + $zone_data = json_decode($this->get("/zones/pull.json/$zone_id"), true); + if (preg_match("(200|201)", $zone_data['code'])) { + return $zone_data['data']['pullzone']; + } else + throw new Exception($this->error_message($zone_data)); + } + + /** + * Deletes files from cache + * @param $zone_id + * @param $files array of relative paths to files to delete + * Deletes whole zone if empty list passed + **/ + public function cache_delete($zone_id, $files = array()) { + if (empty($files)) + $params = array(); + else + $params = array('files' => $files); + + $response = json_decode($this->delete( + '/zones/pull.json/' . $zone_id . '/cache', + $params), true); + + if (preg_match("(200|201)", $response['code'])) { + return true; + } else + throw $this->to_exception($response); + } } diff --git a/lib/S3.php b/lib/S3.php index 3427f24..e89cb78 100644 --- a/lib/S3.php +++ b/lib/S3.php @@ -1,8 +1,8 @@ $host, 'type' => $type, 'user' => $user, 'pass' => $pass); + } + + + /** + * Set the error mode to exceptions + * + * @param boolean $enabled Enable exceptions + * @return void + */ + public static function setExceptions($enabled = true) + { + self::$useExceptions = $enabled; + } + + + /** + * Set AWS time correction offset (use carefully) + * + * This can be used when an inaccurate system time is generating + * invalid request signatures. It should only be used as a last + * resort when the system time cannot be changed. + * + * @param string $offset Time offset (set to zero to use AWS server time) + * @return void + */ + public static function setTimeCorrectionOffset($offset = 0) + { + if ($offset == 0) + { + $rest = new S3Request('HEAD'); + $rest = $rest->getResponse(); + $awstime = $rest->headers['date']; + $systime = time(); + $offset = $systime > $awstime ? -($systime - $awstime) : ($awstime - $systime); + } + self::$__timeOffset = $offset; + } + + + /** + * Set signing key + * + * @param string $keyPairId AWS Key Pair ID + * @param string $signingKey Private Key + * @param boolean $isFile Load private key from file, set to false to load string + * @return boolean + */ + public static function setSigningKey($keyPairId, $signingKey, $isFile = true) + { + self::$__signingKeyPairId = $keyPairId; + if ((self::$__signingKeyResource = openssl_pkey_get_private($isFile ? + file_get_contents($signingKey) : $signingKey)) !== false) return true; + self::triggerError('S3::setSigningKey(): Unable to open load private key: '.$signingKey, __FILE__, __LINE__); + return false; + } + + + /** + * Set Signature Version + * + * @param string $version of signature ('v4' or 'v2') + * @return void + */ + public static function setSignatureVersion($version = 'v2') + { + self::$signVer = $version; + } + + + /** + * Free signing key from memory, MUST be called if you are using setSigningKey() + * + * @return void + */ + public static function freeSigningKey() + { + if (self::$__signingKeyResource !== false) + openssl_free_key(self::$__signingKeyResource); + } + + + /** + * Internal error handler + * + * @internal Internal error handler + * @param string $message Error message + * @param string $file Filename + * @param integer $line Line number + * @param integer $code Error code + * @return void + */ + private static function triggerError($message, $file, $line, $code = 0) + { + if (self::$useExceptions) + throw new S3Exception($message, $file, $line, $code); + else + trigger_error($message, E_USER_WARNING); + } + + /** * Get a list of buckets * * @param boolean $detailed Returns detailed bucket list when true * @return array | false */ - public static function listBuckets($detailed = false) { - $rest = new S3Request('GET', '', '', self::$__api_host); + public static function listBuckets($detailed = false) + { + $rest = new S3Request('GET', '', '', self::$endpoint); $rest = $rest->getResponse(); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::listBuckets(): [%s] %s", - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], + $rest->error['message']), __FILE__, __LINE__); return false; } $results = array(); if (!isset($rest->body->Buckets)) return $results; - if ($detailed) { + if ($detailed) + { if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) $results['owner'] = array( - 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName ); $results['buckets'] = array(); foreach ($rest->body->Buckets->Bucket as $b) @@ -124,6 +482,99 @@ public static function listBuckets($detailed = false) { } + /** + * Get contents for a bucket + * + * If maxKeys is null this method will loop through truncated result sets + * + * @param string $bucket Bucket name + * @param string $prefix Prefix + * @param string $marker Marker (last file listed) + * @param string $maxKeys Max keys (maximum number of keys to return) + * @param string $delimiter Delimiter + * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes + * @return array | false + */ + public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) + { + $rest = new S3Request('GET', $bucket, '', self::$endpoint); + if ($maxKeys == 0) $maxKeys = null; + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); + if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + else if (!empty(self::$defDelimiter)) $rest->setParameter('delimiter', self::$defDelimiter); + $response = $rest->getResponse(); + if ($response->error === false && $response->code !== 200) + $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); + if ($response->error !== false) + { + self::triggerError(sprintf("S3::getBucket(): [%s] %s", + $response->error['code'], $response->error['message']), __FILE__, __LINE__); + return false; + } + + $results = array(); + + $nextMarker = null; + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) + { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->IsTruncated) && + (string)$response->body->IsTruncated == 'false') return $results; + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + // Loop through truncated results if maxKeys isn't specified + if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') + do + { + $rest = new S3Request('GET', $bucket, '', self::$endpoint); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + $rest->setParameter('marker', $nextMarker); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + + if (($response = $rest->getResponse()) == false || $response->code !== 200) break; + + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) + { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + } while ($response !== false && (string)$response->body->IsTruncated == 'true'); + + return $results; + } + + /** * Put a bucket * @@ -132,11 +583,15 @@ public static function listBuckets($detailed = false) { * @param string $location Set as "EU" to create buckets hosted in Europe * @return boolean */ - public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { - $rest = new S3Request('PUT', $bucket, '', self::$__api_host); + public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) + { + $rest = new S3Request('PUT', $bucket, '', self::$endpoint); $rest->setAmzHeader('x-amz-acl', $acl); - if ($location) { + if (empty($location)) $location = self::getRegion(); + + if ($location !== false && $location !== "us-east-1") + { $dom = new DOMDocument; $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); $locationConstraint = $dom->createElement('LocationConstraint', $location); @@ -150,14 +605,32 @@ public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::putBucket('%s', '%s', '%s'): [%s] %s", - $bucket, - $acl, - $location, - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return true; + } + + + /** + * Delete an empty bucket + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function deleteBucket($bucket) + { + $rest = new S3Request('DELETE', $bucket, '', self::$endpoint); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; @@ -171,14 +644,49 @@ public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) * @return array | false */ - private static function inputFile($file, $md5sum = true) { - if (!file_exists($file) || !is_file($file) || !is_readable($file)) { - trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING); + public static function inputFile($file, $md5sum = true) + { + if (!file_exists($file) || !is_file($file) || !is_readable($file)) + { + self::triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__); + return false; + } + clearstatcache(false, $file); + return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ? + (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : '', 'sha256sum' => hash_file('sha256', $file)); + } + + + /** + * Create input array info for putObject() with a resource + * + * @param string $resource Input resource to read from + * @param integer $bufferSize Input byte size + * @param string $md5sum MD5 hash to send (optional) + * @return array | false + */ + public static function inputResource(&$resource, $bufferSize = false, $md5sum = '') + { + if (!is_resource($resource) || (int)$bufferSize < 0) + { + self::triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__); return false; } - return array('file' => $file, 'size' => filesize($file), - 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : - base64_encode(md5_file($file, true))) : ''); + + // Try to figure out the bytesize + if ($bufferSize === false) + { + if (fseek($resource, 0, SEEK_END) < 0 || ($bufferSize = ftell($resource)) === false) + { + self::triggerError('S3::inputResource(): Unable to obtain resource size', __FILE__, __LINE__); + return false; + } + fseek($resource, 0); + } + + $input = array('size' => $bufferSize, 'md5sum' => $md5sum); + $input['fp'] =& $resource; + return $input; } @@ -191,15 +699,19 @@ private static function inputFile($file, $md5sum = true) { * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param array $requestHeaders Array of request headers or content type as a string + * @param constant $storageClass Storage class constant + * @param constant $serverSideEncryption Server-side encryption * @return boolean */ - public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD, $serverSideEncryption = self::SSE_NONE) + { if ($input === false) return false; - $rest = new S3Request('PUT', $bucket, $uri, self::$__api_host); + $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); - if (is_string($input)) $input = array( + if (!is_array($input)) $input = array( 'data' => $input, 'size' => strlen($input), - 'md5sum' => base64_encode(md5($input, true)) + 'md5sum' => base64_encode(md5($input, true)), + 'sha256sum' => hash('sha256', $input) ); // Data @@ -214,33 +726,46 @@ public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE if (isset($input['size']) && $input['size'] >= 0) $rest->size = $input['size']; else { - if (isset($input['file'])) + if (isset($input['file'])) { + clearstatcache(false, $input['file']); $rest->size = filesize($input['file']); + } elseif (isset($input['data'])) $rest->size = strlen($input['data']); } // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) if (is_array($requestHeaders)) - foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + foreach ($requestHeaders as $h => $v) + strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v); elseif (is_string($requestHeaders)) // Support for legacy contentType parameter $input['type'] = $requestHeaders; // Content-Type - if (!isset($input['type'])) { + if (!isset($input['type'])) + { if (isset($requestHeaders['Content-Type'])) $input['type'] =& $requestHeaders['Content-Type']; elseif (isset($input['file'])) - $input['type'] = self::_getMimeType($input['file']); + $input['type'] = self::getMIMEType($input['file']); else $input['type'] = 'application/octet-stream'; } + if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class + $rest->setAmzHeader('x-amz-storage-class', $storageClass); + + if ($serverSideEncryption !== self::SSE_NONE) // Server-side encryption + $rest->setAmzHeader('x-amz-server-side-encryption', $serverSideEncryption); + // We need to post with Content-Length and Content-Type, MD5 is optional - if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { + if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) + { $rest->setHeader('Content-Type', $input['type']); if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); + if (isset($input['sha256sum'])) $rest->setAmzHeader('x-amz-content-sha256', $input['sha256sum']); + $rest->setAmzHeader('x-amz-acl', $acl); foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); $rest->getResponse(); @@ -249,11 +774,10 @@ public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); - if ($rest->response->error !== false) { - trigger_error(sprintf("S3::putObject(): [%s] %s", - $rest->response->error['code'], - $rest->response->error['message'] - ), E_USER_WARNING); + if ($rest->response->error !== false) + { + self::triggerError(sprintf("S3::putObject(): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return true; @@ -271,7 +795,8 @@ public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE * @param string $contentType Content type * @return boolean */ - public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { + public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) + { return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); } @@ -287,7 +812,8 @@ public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIV * @param string $contentType Content type * @return boolean */ - public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { + public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') + { return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); } @@ -300,9 +826,11 @@ public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_ * @param mixed $saveTo Filename or resource to write to * @return mixed */ - public static function getObject($bucket, $uri, $saveTo = false) { - $rest = new S3Request('GET', $bucket, $uri, self::$__api_host); - if ($saveTo !== false) { + public static function getObject($bucket, $uri, $saveTo = false) + { + $rest = new S3Request('GET', $bucket, $uri, self::$endpoint); + if ($saveTo !== false) + { if (is_resource($saveTo)) $rest->fp =& $saveTo; else @@ -315,14 +843,10 @@ public static function getObject($bucket, $uri, $saveTo = false) { if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); - if ($rest->response->error !== false) { - trigger_error(sprintf("S3::getObject('%s', '%s', '%s'): [%s] %s", - $bucket, - $uri, - $saveTo, - $rest->response->error['code'], - $rest->response->error['message'] - ), E_USER_WARNING); + if ($rest->response->error !== false) + { + self::triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return $rest->response; @@ -337,19 +861,16 @@ public static function getObject($bucket, $uri, $saveTo = false) { * @param boolean $returnInfo Return response information * @return mixed | false */ - public static function getObjectInfo($bucket, $uri, $returnInfo = true) { - $rest = new S3Request('HEAD', $bucket, $uri, self::$__api_host); + public static function getObjectInfo($bucket, $uri, $returnInfo = true) + { + $rest = new S3Request('HEAD', $bucket, $uri, self::$endpoint); $rest = $rest->getResponse(); if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::getObjectInfo('%s', '%s', %d): [%s] %s", - $bucket, - $uri, - $returnInfo, - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; @@ -357,24 +878,674 @@ public static function getObjectInfo($bucket, $uri, $returnInfo = true) { /** - * Delete an object + * Copy an object + * + * @param string $srcBucket Source bucket name + * @param string $srcUri Source object URI + * @param string $bucket Destination bucket name + * @param string $uri Destination object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Optional array of x-amz-meta-* headers + * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) + * @param constant $storageClass Storage class constant + * @return mixed | false + */ + public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) + { + $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); + $rest->setHeader('Content-Length', 0); + foreach ($requestHeaders as $h => $v) + strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class + $rest->setAmzHeader('x-amz-storage-class', $storageClass); + $rest->setAmzHeader('x-amz-acl', $acl); + $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri))); + if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) + $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); + + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return isset($rest->body->LastModified, $rest->body->ETag) ? array( + 'time' => strtotime((string)$rest->body->LastModified), + 'hash' => substr((string)$rest->body->ETag, 1, -1) + ) : false; + } + + + /** + * Set up a bucket redirection + * + * @param string $bucket Bucket name + * @param string $location Target host name + * @return boolean + */ + public static function setBucketRedirect($bucket = NULL, $location = NULL) + { + $rest = new S3Request('PUT', $bucket, '', self::$endpoint); + + if( empty($bucket) || empty($location) ) { + self::triggerError("S3::setBucketRedirect({$bucket}, {$location}): Empty parameter.", __FILE__, __LINE__); + return false; + } + + $dom = new DOMDocument; + $websiteConfiguration = $dom->createElement('WebsiteConfiguration'); + $redirectAllRequestsTo = $dom->createElement('RedirectAllRequestsTo'); + $hostName = $dom->createElement('HostName', $location); + $redirectAllRequestsTo->appendChild($hostName); + $websiteConfiguration->appendChild($redirectAllRequestsTo); + $dom->appendChild($websiteConfiguration); + $rest->setParameter('website', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::setBucketRedirect({$bucket}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return true; + } + + + /** + * Set logging for a bucket + * + * @param string $bucket Bucket name + * @param string $targetBucket Target bucket (where logs are stored) + * @param string $targetPrefix Log prefix (e,g; domain.com-) + * @return boolean + */ + public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) + { + // The S3 log delivery group has to be added to the target bucket's ACP + if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) + { + // Only add permissions to the target bucket when they do not exist + $aclWriteSet = false; + $aclReadSet = false; + foreach ($acp['acl'] as $acl) + if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') + { + if ($acl['permission'] == 'WRITE') $aclWriteSet = true; + elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; + } + if (!$aclWriteSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' + ); + if (!$aclReadSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' + ); + if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); + } + + $dom = new DOMDocument; + $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); + $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); + if ($targetBucket !== null) + { + if ($targetPrefix == null) $targetPrefix = $bucket . '-'; + $loggingEnabled = $dom->createElement('LoggingEnabled'); + $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); + $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); + // TODO: Add TargetGrants? + $bucketLoggingStatus->appendChild($loggingEnabled); + } + $dom->appendChild($bucketLoggingStatus); + + $rest = new S3Request('PUT', $bucket, '', self::$endpoint); + $rest->setParameter('logging', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::setBucketLogging({$bucket}, {$targetBucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return true; + } + + + /** + * Get logging status for a bucket + * + * This will return false if logging is not enabled. + * Note: To enable logging, you also need to grant write access to the log group + * + * @param string $bucket Bucket name + * @return array | false + */ + public static function getBucketLogging($bucket) + { + $rest = new S3Request('GET', $bucket, '', self::$endpoint); + $rest->setParameter('logging', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + if (!isset($rest->body->LoggingEnabled)) return false; // No logging + return array( + 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, + 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, + ); + } + + + /** + * Disable bucket logging + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function disableBucketLogging($bucket) + { + return self::setBucketLogging($bucket, null); + } + + + /** + * Get a bucket's location + * + * @param string $bucket Bucket name + * @return string | false + */ + public static function getBucketLocation($bucket) + { + $rest = new S3Request('GET', $bucket, '', self::$endpoint); + $rest->setParameter('location', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; + } + + + /** + * Set object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) + * @return boolean + */ + public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) + { + $dom = new DOMDocument; + $dom->formatOutput = true; + $accessControlPolicy = $dom->createElement('AccessControlPolicy'); + $accessControlList = $dom->createElement('AccessControlList'); + + // It seems the owner has to be passed along too + $owner = $dom->createElement('Owner'); + $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); + $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); + $accessControlPolicy->appendChild($owner); + + foreach ($acp['acl'] as $g) + { + $grant = $dom->createElement('Grant'); + $grantee = $dom->createElement('Grantee'); + $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + if (isset($g['id'])) + { // CanonicalUser (DisplayName is omitted) + $grantee->setAttribute('xsi:type', 'CanonicalUser'); + $grantee->appendChild($dom->createElement('ID', $g['id'])); + } + elseif (isset($g['email'])) + { // AmazonCustomerByEmail + $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); + $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); + } + elseif ($g['type'] == 'Group') + { // Group + $grantee->setAttribute('xsi:type', 'Group'); + $grantee->appendChild($dom->createElement('URI', $g['uri'])); + } + $grant->appendChild($grantee); + $grant->appendChild($dom->createElement('Permission', $g['permission'])); + $accessControlList->appendChild($grant); + } + + $accessControlPolicy->appendChild($accessControlList); + $dom->appendChild($accessControlPolicy); + + $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint); + $rest->setParameter('acl', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return true; + } + + + /** + * Get object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed | false + */ + public static function getAccessControlPolicy($bucket, $uri = '') + { + $rest = new S3Request('GET', $bucket, $uri, self::$endpoint); + $rest->setParameter('acl', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + + $acp = array(); + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) + $acp['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName + ); + + if (isset($rest->body->AccessControlList)) + { + $acp['acl'] = array(); + foreach ($rest->body->AccessControlList->Grant as $grant) + { + foreach ($grant->Grantee as $grantee) + { + if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser + $acp['acl'][] = array( + 'type' => 'CanonicalUser', + 'id' => (string)$grantee->ID, + 'name' => (string)$grantee->DisplayName, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail + $acp['acl'][] = array( + 'type' => 'AmazonCustomerByEmail', + 'email' => (string)$grantee->EmailAddress, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->URI)) // Group + $acp['acl'][] = array( + 'type' => 'Group', + 'uri' => (string)$grantee->URI, + 'permission' => (string)$grant->Permission + ); + else continue; + } + } + } + return $acp; + } + + + /** + * Delete an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return boolean + */ + public static function deleteObject($bucket, $uri) + { + $rest = new S3Request('DELETE', $bucket, $uri, self::$endpoint); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::deleteObject(): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + return true; + } + + + /** + * Get a query string authenticated URL + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param integer $lifetime Lifetime in seconds + * @param boolean $hostBucket Use the bucket name as the hostname + * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) + * @return string + */ + public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) + { + $expires = self::getTime() + $lifetime; + $uri = str_replace(array('%2F', '%2B'), array('/', '+'), rawurlencode($uri)); + return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', + // $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, + $hostBucket ? $bucket : self::$endpoint.'/'.$bucket, $uri, self::$__accessKey, $expires, + urlencode(self::getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); + } + + + /** + * Get a CloudFront signed policy URL + * + * @param array $policy Policy + * @return string + */ + public static function getSignedPolicyURL($policy) + { + $data = json_encode($policy); + $signature = ''; + if (!openssl_sign($data, $signature, self::$__signingKeyResource)) return false; + + $encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data)); + $signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature)); + + $url = $policy['Statement'][0]['Resource'] . '?'; + foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => self::$__signingKeyPairId) as $k => $v) + $url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&'; + return substr($url, 0, -1); + } + + + /** + * Get a CloudFront canned policy URL + * + * @param string $url URL to sign + * @param integer $lifetime URL lifetime + * @return string + */ + public static function getSignedCannedURL($url, $lifetime) + { + return self::getSignedPolicyURL(array( + 'Statement' => array( + array('Resource' => $url, 'Condition' => array( + 'DateLessThan' => array('AWS:EpochTime' => self::getTime() + $lifetime) + )) + ) + )); + } + + + /** + * Get upload POST parameters for form uploads + * + * @param string $bucket Bucket name + * @param string $uriPrefix Object URI prefix + * @param constant $acl ACL constant + * @param integer $lifetime Lifetime in seconds + * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) + * @param string $successRedirect Redirect URL or 200 / 201 status code + * @param array $amzHeaders Array of x-amz-meta-* headers + * @param array $headers Array of request headers or content type as a string + * @param boolean $flashVars Includes additional "Filename" variable posted by Flash + * @return object + */ + public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, + $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) + { + // Create policy object + $policy = new stdClass; + $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (self::getTime() + $lifetime)); + $policy->conditions = array(); + $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); + $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); + + $obj = new stdClass; // 200 for non-redirect uploads + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $obj->success_action_status = (string)$successRedirect; + else // URL + $obj->success_action_redirect = $successRedirect; + array_push($policy->conditions, $obj); + + if ($acl !== self::ACL_PUBLIC_READ) + array_push($policy->conditions, array('eq', '$acl', $acl)); + + array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); + if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); + foreach (array_keys($headers) as $headerKey) + array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); + foreach ($amzHeaders as $headerKey => $headerVal) + { + $obj = new stdClass; + $obj->{$headerKey} = (string)$headerVal; + array_push($policy->conditions, $obj); + } + array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); + $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); + + // Create parameters + $params = new stdClass; + $params->AWSAccessKeyId = self::$__accessKey; + $params->key = $uriPrefix.'${filename}'; + $params->acl = $acl; + $params->policy = $policy; unset($policy); + $params->signature = self::getHash($params->policy); + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $params->success_action_status = (string)$successRedirect; + else + $params->success_action_redirect = $successRedirect; + foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + return $params; + } + + + /** + * Create a CloudFront distribution + * + * @param string $bucket Bucket name + * @param string $originType origin type (s3 or custom) + * @param boolean $enabled Enabled (true/false) + * @param array $cnames Array containing CNAME aliases + * @param string $comment Use the bucket name as the hostname + * @param string $defaultRootObject Default root object + * @param string $originAccessIdentity Origin access identity + * @param array $trustedSigners Array of trusted signers + * @return array | false + */ + public static function createDistribution($bucket, $originType = self::ORIGIN_TYPE_S3, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + $useSSL = self::$useSSL; + + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); + $rest->data = self::getCloudFrontDistributionConfigXML( + $bucket, + $originType, + $enabled, + (string)$comment, + (string)microtime(true), + $cnames, + $defaultRootObject, + $originAccessIdentity, + $trustedSigners + ); + + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = self::getCloudFrontResponse($rest); + + self::$useSSL = $useSSL; + + if ($rest->error === false && $rest->code !== 201) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) + return self::parseCloudFrontDistributionConfig($rest->body); + return false; + } + + + /** + * Get CloudFront distribution info + * + * @param string $distributionId Distribution ID from listDistributions() + * @return array | false + */ + public static function getDistribution($distributionId) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::getDistribution($distributionId): %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + $useSSL = self::$useSSL; + + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); + $rest = self::getCloudFrontResponse($rest); + + self::$useSSL = $useSSL; + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } + elseif ($rest->body instanceof SimpleXMLElement) + { + $dist = self::parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + $dist['id'] = $distributionId; + return $dist; + } + return false; + } + + + /** + * Update a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return array | false + */ + public static function updateDistribution($dist) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + + $useSSL = self::$useSSL; + + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); + $rest->data = self::getCloudFrontDistributionConfigXML( + $dist['origin'], + $dist['type'], + $dist['enabled'], + $dist['comment'], + $dist['callerReference'], + $dist['cnames'], + $dist['defaultRootObject'], + $dist['originAccessIdentity'], + $dist['trustedSigners'] + ); + + $rest->size = strlen($rest->data); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::getCloudFrontResponse($rest); + + self::$useSSL = $useSSL; + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); + return false; + } else { + $dist = self::parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Delete a CloudFront distribution * - * @param string $bucket Bucket name - * @param string $uri Object URI + * @param array $dist Distribution array info identical to output of getDistribution() * @return boolean */ - public static function deleteObject($bucket, $uri) { - $rest = new S3Request('DELETE', $bucket, $uri, self::$__api_host); - $rest = $rest->getResponse(); + public static function deleteDistribution($dist) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + + $useSSL = self::$useSSL; + + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::getCloudFrontResponse($rest); + + self::$useSSL = $useSSL; + if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::deleteObject('%s', '%s'): [%s] %s", - $bucket, - $uri, - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; @@ -382,137 +1553,195 @@ public static function deleteObject($bucket, $uri) { /** - * Create a CloudFront distribution + * Get a list of CloudFront distributions * - * @param string $dnsName Origin DNS name - * @param string $originType Origin Type - * @param boolean $enabled Enabled (true/false) - * @param array $cnames Array containing CNAME aliases - * @param string $comment Use the bucket name as the hostname - * @return array | false + * @return array */ - public static function createDistribution($dnsName, $originType = self::ORIGIN_TYPE_S3, $enabled = true, $cnames = array(), $comment = '') { - self::$use_ssl = true; // CloudFront requires SSL - $rest = new S3Request('POST', '', '2010-11-01/distribution', - 'cloudfront.amazonaws.com'); - $rest->data = self::_getCloudFrontDistributionConfigXML($dnsName, - $originType, $enabled, $comment, (string)microtime(true), $cnames); - $rest->size = strlen($rest->data); - $rest->setHeader('Content-Type', 'application/xml'); - $rest = self::_getCloudFrontResponse($rest); + public static function listDistributions() + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::listDistributions(): [%s] %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } - if ($rest->error === false && $rest->code !== 201) + $useSSL = self::$useSSL; + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); + $rest = self::getCloudFrontResponse($rest); + self::$useSSL = $useSSL; + + if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::createDistribution('%s', '%s', %d, '%s', '%s'): [%s] %s", - $dnsName, - $originType, - $enabled, - implode(', ', $cnames), - $comment, - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + self::triggerError(sprintf("S3::listDistributions(): [%s] %s", + $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; - } elseif ($rest->body instanceof SimpleXMLElement) - return self::_parseCloudFrontDistributionConfig($rest->body); - return false; + } + elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) + { + $list = array(); + if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) + { + //$info['marker'] = (string)$rest->body->Marker; + //$info['maxItems'] = (int)$rest->body->MaxItems; + //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; + } + foreach ($rest->body->DistributionSummary as $summary) + $list[(string)$summary->Id] = self::parseCloudFrontDistributionConfig($summary); + + return $list; + } + return array(); } /** - * Get CloudFront distribution info + * List CloudFront Origin Access Identities * - * @param string $distributionId Distribution ID from listDistributions() - * @return array | false + * @return array */ - public static function getDistribution($distributionId) { - self::$use_ssl = true; // CloudFront requires SSL - $rest = new S3Request('GET', '', - '2010-11-01/distribution/' . $distributionId, - 'cloudfront.amazonaws.com'); - $rest = self::_getCloudFrontResponse($rest); + public static function listOriginAccessIdentities() + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com'); + $rest = self::getCloudFrontResponse($rest); + $useSSL = self::$useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::getDistribution(%d): [%s] %s", - $distributionId, - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; - } elseif ($rest->body instanceof SimpleXMLElement) { - $dist = self::_parseCloudFrontDistributionConfig($rest->body); - $dist['hash'] = $rest->headers['hash']; - return $dist; + } + + if (isset($rest->body->CloudFrontOriginAccessIdentitySummary)) + { + $identities = array(); + foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity) + if (isset($identity->S3CanonicalUserId)) + $identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId); + return $identities; } return false; } /** - * Update a CloudFront distribution + * Invalidate objects in a CloudFront distribution * - * @param array $dist Distribution array info identical to output of getDistribution() - * @return array | false + * Thanks to Martin Lindkvist for S3::invalidateDistribution() + * + * @param string $distributionId Distribution ID from listDistributions() + * @param array $paths Array of object paths to invalidate + * @return boolean */ - public static function updateDistribution($dist) { - self::$use_ssl = true; // CloudFront requires SSL - $rest = new S3Request('PUT', '', - '2010-11-01/distribution/' . $dist['id'] . '/config', - 'cloudfront.amazonaws.com'); - $rest->data = self::_getCloudFrontDistributionConfigXML($dist['origin'], $dist['type'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']); + public static function invalidateDistribution($distributionId, $paths) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::invalidateDistribution(): [%s] %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + + $useSSL = self::$useSSL; + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); + $rest->data = self::getCloudFrontInvalidationBatchXML($paths, (string)microtime(true)); $rest->size = strlen($rest->data); - $rest->setHeader('If-Match', $dist['hash']); - $rest = self::_getCloudFrontResponse($rest); + $rest = self::getCloudFrontResponse($rest); + self::$useSSL = $useSSL; - if ($rest->error === false && $rest->code !== 200) + if ($rest->error === false && $rest->code !== 201) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::updateDistribution('%s'): [%s] %s", - serialize($dist), - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + trigger_error(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; - } else { - $dist = self::_parseCloudFrontDistributionConfig($rest->body); - $dist['hash'] = $rest->headers['hash']; - return $dist; } - return false; + return true; } /** - * Get a list of CloudFront distributions + * Get a InvalidationBatch DOMDocument + * + * @internal Used to create XML in invalidateDistribution() + * @param array $paths Paths to objects to invalidateDistribution + * @param int $callerReference + * @return string + */ + private static function getCloudFrontInvalidationBatchXML($paths, $callerReference = '0') + { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $invalidationBatch = $dom->createElement('InvalidationBatch'); + foreach ($paths as $path) + $invalidationBatch->appendChild($dom->createElement('Path', $path)); + + $invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference)); + $dom->appendChild($invalidationBatch); + return $dom->saveXML(); + } + + + /** + * List your invalidation batches for invalidateDistribution() in a CloudFront distribution + * + * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/ListInvalidation.html + * returned array looks like this: + * Array + * ( + * [I31TWB0CN9V6XD] => InProgress + * [IT3TFE31M0IHZ] => Completed + * [I12HK7MPO1UQDA] => Completed + * [I1IA7R6JKTC3L2] => Completed + * ) * + * @param string $distributionId Distribution ID from listDistributions() * @return array */ - public static function listDistributions() { - self::$use_ssl = true; // CloudFront requires SSL - $rest = new S3Request('GET', '', '2010-11-01/distribution', - 'cloudfront.amazonaws.com'); - $rest = self::_getCloudFrontResponse($rest); + public static function getDistributionInvalidationList($distributionId) + { + if (!extension_loaded('openssl')) + { + self::triggerError(sprintf("S3::getDistributionInvalidationList(): [%s] %s", + "CloudFront functionality requires SSL"), __FILE__, __LINE__); + return false; + } + + $useSSL = self::$useSSL; + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); + $rest = self::getCloudFrontResponse($rest); + self::$useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - if ($rest->error !== false) { - trigger_error(sprintf("S3::listDistributions(): [%s] %s", - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); + if ($rest->error !== false) + { + trigger_error(sprintf("S3::getDistributionInvalidationList('{$distributionId}'): [%s]", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; - } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { + } + elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->InvalidationSummary)) + { $list = array(); - if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { - //$info['marker'] = (string)$rest->body->Marker; - //$info['maxItems'] = (int)$rest->body->MaxItems; - //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; - } - foreach ($rest->body->DistributionSummary as $summary) { - $list[(string)$summary->Id] = self::_parseCloudFrontDistributionConfig($summary); - } + foreach ($rest->body->InvalidationSummary as $summary) + $list[(string)$summary->Id] = (string)$summary->Status; + return $list; } return array(); @@ -522,45 +1751,56 @@ public static function listDistributions() { /** * Get a DistributionConfig DOMDocument * + * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html + * * @internal Used to create XML in createDistribution() and updateDistribution() - * @param string $dnsName Origin DNS name - * @param string $originType Origin type + * @param string $bucket S3 Origin bucket + * @param string $originType origin type (s3 or custom) * @param boolean $enabled Enabled (true/false) * @param string $comment Comment to append * @param string $callerReference Caller reference * @param array $cnames Array of CNAME aliases + * @param string $defaultRootObject Default root object + * @param string $originAccessIdentity Origin access identity + * @param array $trustedSigners Array of trusted signers * @return string */ - private static function _getCloudFrontDistributionConfigXML($dnsName, $originType, $enabled, $comment, $callerReference = '0', $cnames = array()) { + private static function getCloudFrontDistributionConfigXML($bucket, $originType, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) + { $dom = new DOMDocument('1.0', 'UTF-8'); - - $dom->formatOutput = true; + $dom->formatOutput = true; $distributionConfig = $dom->createElement('DistributionConfig'); $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/'); - if ($originType == 's3') { + if ($originType == self::ORIGIN_TYPE_S3) { $origin = $dom->createElement('S3Origin'); - $origin->appendChild($dom->createElement('DNSName', $dnsName)); + $origin->appendChild($dom->createElement('DNSName', $bucket)); } else { $origin = $dom->createElement('CustomOrigin'); - $origin->appendChild($dom->createElement('DNSName', $dnsName)); + $origin->appendChild($dom->createElement('DNSName', $bucket)); $origin->appendChild($dom->createElement('OriginProtocolPolicy', 'http-only')); } + if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity)); $distributionConfig->appendChild($origin); - $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); - foreach ($cnames as $cname) { + if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject)); + + $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); + foreach ($cnames as $cname) $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); - } + if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); - if ($comment !== '') { - $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + if (!empty($trustedSigners)) { + $trusted = $dom->createElement('TrustedSigners'); + foreach ($trustedSigners as $id => $type) + $trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type)); + $distributionConfig->appendChild($trusted); } - $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); $dom->appendChild($distributionConfig); - + //var_dump($dom->saveXML()); return $dom->saveXML(); } @@ -568,170 +1808,77 @@ private static function _getCloudFrontDistributionConfigXML($dnsName, $originTyp /** * Parse a CloudFront distribution config * + * See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html + * * @internal Used to parse the CloudFront DistributionConfig node to an array * @param object &$node DOMNode * @return array */ - private static function _parseCloudFrontDistributionConfig(&$node) { - $dist = array(); + private static function parseCloudFrontDistributionConfig(&$node) + { + if (isset($node->DistributionConfig)) + return self::parseCloudFrontDistributionConfig($node->DistributionConfig); - if (isset($node->Id)) { - $dist['id'] = (string) $node->Id; - } - - if (isset($node->Status)) { - $dist['status'] = (string) $node->Status; - } + $dist = array(); + if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) + { + $dist['id'] = (string)$node->Id; + $dist['status'] = (string)$node->Status; + $dist['time'] = strtotime((string)$node->LastModifiedTime); + $dist['domain'] = (string)$node->DomainName; + } - if (isset($node->LastModifiedTime)) { - $dist['time'] = strtotime((string) $node->LastModifiedTime); - } + if (isset($node->CallerReference)) + $dist['callerReference'] = (string)$node->CallerReference; - if (isset($node->DomainName)) { - $dist['domain'] = (string) $node->DomainName; - } + if (isset($node->Enabled)) + $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; - if (isset($node->S3Origin)) { - $dist['type'] = 's3'; + if (isset($node->S3Origin)) + { + $dist['type'] = self::ORIGIN_TYPE_S3; if (isset($node->S3Origin->DNSName)) { - $dist['origin'] = (string) $node->S3Origin->DNSName; + $dist['origin'] = (string)$node->S3Origin->DNSName; } + + $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ? + (string)$node->S3Origin->OriginAccessIdentity : null; } elseif (isset($node->CustomOrigin)) { - $dist['type'] = 'custom'; + $dist['type'] = self::ORIGIN_TYPE_CUSTOM; if (isset($node->CustomOrigin->DNSName)) { $dist['origin'] = (string) $node->CustomOrigin->DNSName; } - } - - if (isset($node->CallerReference)) { - $dist['callerReference'] = (string) $node->CallerReference; - } - - if (isset($node->CNAME)) { - $dist['cnames'] = array(); - - foreach ($node->CNAME as $cname) { - $dist['cnames'][] = (string) $cname; - } - } - - if (isset($node->Comment)) { - $dist['comment'] = (string) $node->Comment; - } - if (isset($node->Enabled)) { - $dist['enabled'] = ((string) $node->Enabled == 'true' ? true : false); + $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ? + (string)$node->S3Origin->OriginAccessIdentity : null; } - if (isset($node->DistributionConfig)) { - $dist = array_merge($dist, self::_parseCloudFrontDistributionConfig($node->DistributionConfig)); - } + $dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null; + + $dist['cnames'] = array(); + if (isset($node->CNAME)) + foreach ($node->CNAME as $cname) + $dist['cnames'][(string)$cname] = (string)$cname; + + $dist['trustedSigners'] = array(); + if (isset($node->TrustedSigners)) + foreach ($node->TrustedSigners as $signer) + { + if (isset($signer->Self)) + $dist['trustedSigners'][''] = 'Self'; + elseif (isset($signer->KeyPairId)) + $dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId'; + elseif (isset($signer->AwsAccountNumber)) + $dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber'; + } + $dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null; return $dist; } - /** - * Creates invalidation bath - * - * @static - * @param integer $distributionId - * @param array $paths - * @return array|bool - */ - public static function createInvalidation($distributionId, $paths) { - self::$use_ssl = true; // CloudFront requires SSL - - $rest = new S3Request('POST', '', - '2010-11-01/distribution/' . $distributionId . '/invalidation', - 'cloudfront.amazonaws.com'); - - $rest->data = self::_getCloudFrontInvalidationBath($paths); - $rest->size = strlen($rest->data); - $rest->setHeader('Content-Type', 'application/xml'); - - $rest = self::_getCloudFrontResponse($rest); - - if ($rest->error === false && $rest->code !== 201) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - - if ($rest->error !== false) { - trigger_error(sprintf("S3::createInvalidation(%d, '%s'): [%s] %s", - $distributionId, - implode(', ', $paths), - $rest->error['code'], - $rest->error['message'] - ), E_USER_WARNING); - return false; - } elseif ($rest->body instanceof SimpleXMLElement) { - return self::_parseCloudFrontInvalidation($rest->body); - } - - return false; - } - - - /** - * Returns invalidation bath XML - * - * @static - * @param array $files - * @return string - */ - private static function _getCloudFrontInvalidationBath($paths) { - $dom = new DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - - $invalidationBath = $dom->createElement('InvalidationBatch'); - - foreach ($paths as $path) { - $invalidationBath->appendChild($dom->createElement('Path', $path)); - } - - $invalidationBath->appendChild($dom->createElement('CallerReference', date('YmdHis'))); - $dom->appendChild($invalidationBath); - - return $dom->saveXML(); - } - - - /** - * Parse CloudFront invalidation XML - * - * @static - * @param DOMNode $node - * @return array - */ - private static function _parseCloudFrontInvalidation(&$node) { - $invalidation = array(); - - if (isset($node->Id)) { - $invalidation['id'] = $node->Id; - } - - if (isset($node->Status)) { - $invalidation['status'] = $node->Status; - } - - if (isset($node->CreateTime)) { - $invalidation['createTime'] = $node->CreateTime; - } - - if (isset($node->InvalidationBatch)) { - $invalidation['invalidationBath'] = array(); - - foreach ($node->InvalidationBatch as $path) { - $invalidation['invalidationBath'][] = $path; - } - } - - return $invalidation; - } - - /** * Grab CloudFront response * @@ -739,14 +1886,17 @@ private static function _parseCloudFrontInvalidation(&$node) { * @param object &$rest S3Request instance * @return object */ - private static function _getCloudFrontResponse(&$rest) { + private static function getCloudFrontResponse(&$rest) + { $rest->getResponse(); if ($rest->response->error === false && isset($rest->response->body) && - is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); // Grab CloudFront errors if (isset($rest->response->body->Error, $rest->response->body->Error->Code, - $rest->response->body->Error->Message)) { + $rest->response->body->Error->Message)) + { $rest->response->error = array( 'code' => (string)$rest->response->body->Error->Code, 'message' => (string)$rest->response->body->Error->Message @@ -761,13 +1911,30 @@ private static function _getCloudFrontResponse(&$rest) { /** * Get MIME type for file * + * To override the putObject() Content-Type, add it to $requestHeaders + * + * To use fileinfo, ensure the MAGIC environment variable is set + * * @internal Used to get mime types * @param string &$file File path * @return string */ - public static function _getMimeType(&$file) { - $type = Util_Mime::get_mime_type($file); - return $type; + private static function getMIMEType(&$file) { + $type = Util_Mime::get_mime_type($file); + return $type; + } + + + /** + * Get the current time + * + * @internal Used to apply offsets to sytem time + * @return integer + */ + public static function getTime() + { + self::setTimeCorrectionOffset(); // Set correct offset + return time() + self::$__timeOffset; } @@ -778,8 +1945,9 @@ public static function _getMimeType(&$file) { * @param string $string String to sign * @return string */ - public static function _getSignature($string) { - return 'AWS '.self::$__accessKey.':'.self::_getHash($string); + public static function getSignature($string) + { + return 'AWS '.self::$__accessKey.':'.self::getHash($string); } @@ -788,11 +1956,12 @@ public static function _getSignature($string) { * * This uses the hash extension if loaded * - * @internal Used by _getSignature() + * @internal Used by getSignature() * @param string $string String to sign * @return string */ - private static function _getHash($string) { + private static function getHash($string) + { return base64_encode(extension_loaded('hash') ? hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . @@ -800,55 +1969,260 @@ private static function _getHash($string) { (str_repeat(chr(0x36), 64))) . $string))))); } -} + /** + * Generate the headers for AWS Signature V4 + * @internal Used by S3Request::getResponse() + * @param array $aheaders amzHeaders + * @param array $headers + * @param string $method + * @param string $uri + * @param string $data + * @param array $parameters + * @return array $headers + */ + public static function getSignatureV4($aHeaders, $headers, $method='GET', $uri='', $data = '', $parameters=array()) + { + $service = self::ORIGIN_TYPE_S3; + $region = S3::getRegion(); + + $algorithm = 'AWS4-HMAC-SHA256'; + $amzHeaders = array(); + $amzRequests = array(); + + $amzDate = gmdate( 'Ymd\THis\Z' ); + $amzDateStamp = gmdate( 'Ymd' ); + + // amz-date ISO8601 format ? for aws request + $amzHeaders['x-amz-date'] = $amzDate; + + // CanonicalHeaders + foreach ( $headers as $k => $v ) { + $amzHeaders[ strtolower( $k ) ] = trim( $v ); + } + foreach ( $aHeaders as $k => $v ) { + $amzHeaders[ strtolower( $k ) ] = trim( $v ); + } + uksort( $amzHeaders, 'strcmp' ); + + // payload + $payloadHash = isset($amzHeaders['x-amz-content-sha256']) ? $amzHeaders['x-amz-content-sha256'] : hash('sha256', $data); + + $uriQmPos = strpos($uri, '?'); + + // CanonicalRequests + $amzRequests[] = $method; + $amzRequests[] = ($uriQmPos === false ? $uri : substr($uri, 0, $uriQmPos)); + $amzRequests[] = http_build_query($parameters); + // add header as string to requests + foreach ( $amzHeaders as $k => $v ) { + $amzRequests[] = $k . ':' . $v; + } + // add a blank entry so we end up with an extra line break + $amzRequests[] = ''; + // SignedHeaders + $amzRequests[] = implode( ';', array_keys( $amzHeaders ) ); + // payload hash + $amzRequests[] = $payloadHash; + // request as string + $amzRequestStr = implode("\n", $amzRequests); + + // CredentialScope + $credentialScope = array(); + $credentialScope[] = $amzDateStamp; + $credentialScope[] = $region; + $credentialScope[] = $service; + $credentialScope[] = 'aws4_request'; + + // stringToSign + $stringToSign = array(); + $stringToSign[] = $algorithm; + $stringToSign[] = $amzDate; + $stringToSign[] = implode('/', $credentialScope); + $stringToSign[] = hash('sha256', $amzRequestStr); + // as string + $stringToSignStr = implode("\n", $stringToSign); + + // Make Signature + $kSecret = 'AWS4' . self::$__secretKey; + $kDate = hash_hmac( 'sha256', $amzDateStamp, $kSecret, true ); + $kRegion = hash_hmac( 'sha256', $region, $kDate, true ); + $kService = hash_hmac( 'sha256', $service, $kRegion, true ); + $kSigning = hash_hmac( 'sha256', 'aws4_request', $kService, true ); + + $signature = hash_hmac( 'sha256', $stringToSignStr, $kSigning ); + + $authorization = array( + 'Credential=' . self::$__accessKey . '/' . implode( '/', $credentialScope ), + 'SignedHeaders=' . implode( ';', array_keys( $amzHeaders ) ), + 'Signature=' . $signature, + ); + + $authorizationStr = $algorithm . ' ' . implode( ',', $authorization ); + + $resultHeaders = array( + 'X-AMZ-DATE' => $amzDate, + 'Authorization' => $authorizationStr + ); + if (!isset($aHeaders['x-amz-content-sha256'])) $resultHeaders['x-amz-content-sha256'] = $payloadHash; + + return $resultHeaders; + } + + +} +/** + * S3 Request class + * + * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class + * @version 0.5.0-dev + */ +final class S3Request +{ + /** + * AWS URI + * + * @var string + * @access private + */ + private $endpoint; -class S3Request { + /** + * Verb + * + * @var string + * @access private + */ private $verb; - private $bucket; - private $uri; - private $resource = ''; - private $parameters = array(); - private $amzHeaders = array(); - - public $fp = false; - public $size = 0; - public $data = false; - public $response; - public $headers = array( - 'Host' => '', - 'Date' => '', - 'Content-MD5' => '', - 'Content-Type' => '' + + /** + * S3 bucket name + * + * @var string + * @access private + */ + private $bucket; + + /** + * Object URI + * + * @var string + * @access private + */ + private $uri; + + /** + * Final object URI + * + * @var string + * @access private + */ + private $resource = ''; + + /** + * Additional request parameters + * + * @var array + * @access private + */ + private $parameters = array(); + + /** + * Amazon specific request headers + * + * @var array + * @access private + */ + private $amzHeaders = array(); + + /** + * HTTP request headers + * + * @var array + * @access private + */ + private $headers = array( + 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' ); + /** + * Use HTTP PUT? + * + * @var bool + * @access public + */ + public $fp = false; + + /** + * PUT file size + * + * @var int + * @access public + */ + public $size = 0; + + /** + * PUT post fields + * + * @var array + * @access public + */ + public $data = false; + + /** + * S3 request respone + * + * @var object + * @access public + */ + public $response; + + /** * Constructor * * @param string $verb Verb * @param string $bucket Bucket name * @param string $uri Object URI + * @param string $endpoint AWS endpoint URI * @return mixed */ - function __construct($verb, $bucket = '', $uri = '', - $api_host = 's3.amazonaws.com') { + function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com') + { + + $this->endpoint = $endpoint; $this->verb = $verb; - $this->bucket = strtolower($bucket); + $this->bucket = $bucket; $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; - if ($this->bucket !== '') { - $this->headers['Host'] = $this->bucket.'.'.$api_host; - $this->resource = '/'.$this->bucket.$this->uri; - } else { - $this->headers['Host'] = $api_host; - //$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri; + if ($this->bucket !== '') + { + if ($this->dnsBucketName($this->bucket)) + { + $this->headers['Host'] = $this->bucket.'.'.$this->endpoint; + $this->resource = '/'.$this->bucket.$this->uri; + } + else + { + $this->headers['Host'] = $this->endpoint; + if ($this->bucket !== '') $this->uri = '/'.$this->bucket.$this->uri; + $this->bucket = ''; + $this->resource = $this->uri; + } + } + else + { + $this->headers['Host'] = $this->endpoint; $this->resource = $this->uri; } - $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); $this->response = new STDClass; $this->response->error = false; + $this->response->body = null; + $this->response->headers = array(); } @@ -859,7 +2233,8 @@ function __construct($verb, $bucket = '', $uri = '', * @param string $value Value * @return void */ - public function setParameter($key, $value) { + public function setParameter($key, $value) + { $this->parameters[$key] = $value; } @@ -871,7 +2246,8 @@ public function setParameter($key, $value) { * @param string $value Value * @return void */ - public function setHeader($key, $value) { + public function setHeader($key, $value) + { $this->headers[$key] = $value; } @@ -883,7 +2259,8 @@ public function setHeader($key, $value) { * @param string $value Value * @return void */ - public function setAmzHeader($key, $value) { + public function setAmzHeader($key, $value) + { $this->amzHeaders[$key] = $value; } @@ -893,13 +2270,14 @@ public function setAmzHeader($key, $value) { * * @return object | false */ - public function getResponse() { + public function getResponse() + { $query = ''; - if (sizeof($this->parameters) > 0) { + if (sizeof($this->parameters) > 0) + { $query = substr($this->uri, -1) !== '?' ? '?' : '&'; foreach ($this->parameters as $var => $value) if ($value == null || $value == '') $query .= $var.'&'; - // Parameters should be encoded (thanks Sean O'Dea) else $query .= $var.'='.rawurlencode($value).'&'; $query = substr($query, 0, -1); $this->uri .= $query; @@ -907,24 +2285,42 @@ public function getResponse() { if (array_key_exists('acl', $this->parameters) || array_key_exists('location', $this->parameters) || array_key_exists('torrent', $this->parameters) || + array_key_exists('website', $this->parameters) || array_key_exists('logging', $this->parameters)) $this->resource .= $query; } - $url = ((S3::$use_ssl && extension_loaded('openssl')) ? - 'https://':'http://').$this->headers['Host'].$this->uri; - //var_dump($this->bucket, $this->uri, $this->resource, $url); + $url = (S3::$useSSL ? 'https://' : 'http://') . ($this->headers['Host'] !== '' ? $this->headers['Host'] : $this->endpoint) . $this->uri; + + //var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url); // Basic setup $curl = curl_init(); curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); - if (S3::$use_ssl) { - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + if (S3::$useSSL) + { + // Set protocol version + curl_setopt($curl, CURLOPT_SSLVERSION, S3::$useSSLVersion); + + // SSL Validation can now be optional for those with broken OpenSSL installations + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, S3::$useSSLValidation ? 2 : 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, S3::$useSSLValidation ? 1 : 0); + + if (S3::$sslKey !== null) curl_setopt($curl, CURLOPT_SSLKEY, S3::$sslKey); + if (S3::$sslCert !== null) curl_setopt($curl, CURLOPT_SSLCERT, S3::$sslCert); + if (S3::$sslCACert !== null) curl_setopt($curl, CURLOPT_CAINFO, S3::$sslCACert); } curl_setopt($curl, CURLOPT_URL, $url); + if (S3::$proxy != null && isset(S3::$proxy['host'])) + { + curl_setopt($curl, CURLOPT_PROXY, S3::$proxy['host']); + curl_setopt($curl, CURLOPT_PROXYTYPE, S3::$proxy['type']); + if (isset(S3::$proxy['user'], S3::$proxy['pass']) && S3::$proxy['user'] != null && S3::$proxy['pass'] != null) + curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', S3::$proxy['user'], S3::$proxy['pass'])); + } + // Headers $headers = array(); $amz = array(); foreach ($this->amzHeaders as $header => $value) @@ -937,44 +2333,70 @@ public function getResponse() { if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; // AMZ headers must be sorted - if (sizeof($amz) > 0) { - sort($amz); + if (sizeof($amz) > 0) + { + //sort($amz); + usort($amz, array(&$this, 'sortMetaHeadersCmp')); $amz = "\n".implode("\n", $amz); } else $amz = ''; - // Authorization string (CloudFront stringToSign should only contain a date) - $headers[] = 'Authorization: ' . S3::_getSignature( - $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : - $this->verb . "\n" . - $this->headers['Content-MD5'] . "\n" . - $this->headers['Content-Type'] . "\n" . - $this->headers['Date'] . - $amz . "\n" . - $this->resource - ); + if (S3::hasAuth()) + { + // Authorization string (CloudFront stringToSign should only contain a date) + if ($this->headers['Host'] == 'cloudfront.amazonaws.com') + $headers[] = 'Authorization: ' . S3::getSignature($this->headers['Date']); + else + { + if (S3::$signVer == 'v2') + { + $headers[] = 'Authorization: ' . S3::getSignature( + $this->verb."\n". + $this->headers['Content-MD5']."\n". + $this->headers['Content-Type']."\n". + $this->headers['Date'].$amz."\n". + $this->resource + ); + } else { + $amzHeaders = S3::getSignatureV4( + $this->amzHeaders, + $this->headers, + $this->verb, + $this->uri, + $this->data, + $this->parameters + ); + foreach ($amzHeaders as $k => $v) { + $headers[] = $k .': '. $v; + } + } + } + } curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); - curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '_responseWriteCallback')); - curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '_responseHeaderCallback')); - @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, 'responseWriteCallback')); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, 'responseHeaderCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Request types - switch ($this->verb) { + switch ($this->verb) + { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront - if ($this->fp !== false) { + if ($this->fp !== false) + { curl_setopt($curl, CURLOPT_PUT, true); curl_setopt($curl, CURLOPT_INFILE, $this->fp); if ($this->size >= 0) curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); - } elseif ($this->data !== false) { + } + elseif ($this->data !== false) + { curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); - if ($this->size >= 0) - curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size); - } else + } + else curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); break; case 'HEAD': @@ -1001,12 +2423,14 @@ public function getResponse() { // Parse body into XML if ($this->response->error === false && isset($this->response->headers['type']) && - $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { + $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) + { $this->response->body = simplexml_load_string($this->response->body); // Grab S3 errors - if (!in_array($this->response->code, array(200, 204)) && - isset($this->response->body->Code, $this->response->body->Message)) { + if (!in_array($this->response->code, array(200, 204, 206)) && + isset($this->response->body->Code, $this->response->body->Message)) + { $this->response->error = array( 'code' => (string)$this->response->body->Code, 'message' => (string)$this->response->body->Message @@ -1023,6 +2447,24 @@ public function getResponse() { return $this->response; } + /** + * Sort compare for meta headers + * + * @internal Used to sort x-amz meta headers + * @param string $a String A + * @param string $b String B + * @return integer + */ + private function sortMetaHeadersCmp($a, $b) + { + $lenA = strpos($a, ':'); + $lenB = strpos($b, ':'); + $minLen = min($lenA, $lenB); + $ncmp = strncmp($a, $b, $minLen); + if ($lenA == $lenB) return $ncmp; + if (0 == $ncmp) return $lenA < $lenB ? -1 : 1; + return $ncmp; + } /** * CURL write callback @@ -1031,39 +2473,95 @@ public function getResponse() { * @param string &$data Data * @return integer */ - private function _responseWriteCallback(&$curl, &$data) { - if ($this->response->code == 200 && $this->fp !== false) + private function responseWriteCallback(&$curl, &$data) + { + if (in_array($this->response->code, array(200, 206)) && $this->fp !== false) return fwrite($this->fp, $data); else - $this->response->body .= $data; + { + if (isset($this->response->body)) + $this->response->body .= $data; + else + $this->response->body = $data; + } return strlen($data); } + /** + * Check DNS conformity + * + * @param string $bucket Bucket name + * @return boolean + */ + private function dnsBucketName($bucket) + { + if (strlen($bucket) > 63 || preg_match("/[^a-z0-9\.-]/", $bucket) > 0) return false; + if (S3::$useSSL && strstr($bucket, '.') !== false) return false; + if (strstr($bucket, '-.') !== false) return false; + if (strstr($bucket, '..') !== false) return false; + if (!preg_match("/^[0-9a-z]/", $bucket)) return false; + if (!preg_match("/[0-9a-z]$/", $bucket)) return false; + return true; + } + + /** * CURL header callback * - * @param resource &$curl CURL resource - * @param string &$data Data + * @param resource $curl CURL resource + * @param string $data Data * @return integer */ - private function _responseHeaderCallback(&$curl, &$data) { + private function responseHeaderCallback($curl, $data) + { if (($strlen = strlen($data)) <= 2) return $strlen; if (substr($data, 0, 4) == 'HTTP') $this->response->code = (int)substr($data, 9, 3); - else { - list($header, $value) = explode(': ', trim($data), 2); - if ($header == 'Last-Modified') + else + { + $data = trim($data); + if (strpos($data, ': ') === false) return $strlen; + list($header, $value) = explode(': ', $data, 2); + $header = strtolower($header); + if ($header == 'last-modified') $this->response->headers['time'] = strtotime($value); - elseif ($header == 'Content-Length') + elseif ($header == 'date') + $this->response->headers['date'] = strtotime($value); + elseif ($header == 'content-length') $this->response->headers['size'] = (int)$value; - elseif ($header == 'Content-Type') + elseif ($header == 'content-type') $this->response->headers['type'] = $value; - elseif ($header == 'ETag') + elseif ($header == 'etag') $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; elseif (preg_match('/^x-amz-meta-.*$/', $header)) - $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; + $this->response->headers[$header] = $value; } return $strlen; } + +} + +/** + * S3 exception class + * + * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class + * @version 0.5.0-dev + */ + +class S3Exception extends Exception { + /** + * Class constructor + * + * @param string $message Exception message + * @param string $file File in which exception was created + * @param string $line Line number on which exception was created + * @param int $code Exception code + */ + function __construct($message, $file, $line, $code = 0) + { + parent::__construct($message, $code); + $this->file = $file; + $this->line = $line; + } } diff --git a/pub/css/lightbox.css b/pub/css/lightbox.css index dbfc113..385bbc4 100644 --- a/pub/css/lightbox.css +++ b/pub/css/lightbox.css @@ -1,3 +1,12 @@ +#w3tc_lightbox_content .metabox-holder { + padding-top: 0; +} + +/* make mismatch in popup resizing invisible, border shows them well */ +#w3tc_lightbox_content .postbox { + border: none; +} + #overlay { background: #666; font-size:12px; diff --git a/pub/css/options.css b/pub/css/options.css index aa25b6d..2be0fb9 100644 --- a/pub/css/options.css +++ b/pub/css/options.css @@ -1,331 +1,338 @@ #w3tc h2.logo { - width: 224px; - height: 44px; - background: url("../img/W3TC_dashboard_logo_title.png") top right no-repeat; - text-indent: -9999px; - margin:5px 0 10px 0; - padding:0; + width: 224px; + height: 44px; + background: url("../img/W3TC_dashboard_logo_title.png") top right no-repeat; + text-indent: -9999px; + margin:5px 0 10px 0; + padding:0; } #icon-w3tc_general { - display: none; + display: none; } .w3tc-enabled { - color: #090; - font-weight: 700 + color: #090; + font-weight: 700 } .w3tc-disabled { - color: #f00; - font-weight: 700 + color: #f00; + font-weight: 700 } .w3tc-empty { - font-weight: 700; - font-style: italic + font-weight: 700; + font-style: italic } .w3tc-success { - background: #bfb + background: #bfb } .w3tc-error { - background: #f99 + background: #f99 } div.w3tc-error.inline { - display:inline-block; + display:inline-block; } input.w3tc-error, textarea.w3tc-error { - background: none; - border: 1px solid #f99 + background: none; + border: 1px solid #f99 } .w3tc-status { - padding: 5px + padding: 5px } #w3tc-help ul { - float: left; - width: 29%; - list-style-type: disc; - margin: 1em 2% 1em 2%; + float: left; + width: 29%; + list-style-type: disc; + margin: 1em 2% 1em 2%; } #w3tc-help li { - margin: 0; + margin: 0; } #w3tc-help a { - text-decoration: none; + text-decoration: none; } #w3tc-help a:hover { - text-decoration: underline; + text-decoration: underline; } #w3tc acronym { - border-bottom: 1px dotted #666 + border-bottom: 1px dotted #666 } #w3tc ul { - list-style: disc outside; - margin-left: 15px; - margin-top: 0; - margin-bottom: 0; + list-style: disc outside; + margin-left: 15px; + margin-top: 0; + margin-bottom: 0; } #w3tc ul.w3tc-incomp-plugins { - list-style: disc outside; - margin-left: 17px; - margin-top: 0; - margin-bottom: 0; + list-style: disc outside; + margin-left: 17px; + margin-top: 0; + margin-bottom: 0; } #w3tc ul.w3tc-incomp-plugins li div{ - width: 150px; - display: inline-block; + width: 150px; + display: inline-block; } #w3tc blockquote { - font-style: italic; + font-style: italic; } #w3tc blockquote cite { - font-weight: 400; + font-weight: 400; } #w3tc optgroup { - font-style: normal; + font-style: normal; } #w3tc optgroup option { - text-indent: 20px; + text-indent: 20px; } #w3tc h5 { - margin: 0 + margin: 0 } #w3tc hr { - clear: both; - margin-top: 10px + clear: both; + margin-top: 10px } #w3tc #toc a, #qa a { - text-decoration: none + text-decoration: none } #w3tc #toc a:hover, #qa a:hover { - text-decoration: underline + text-decoration: underline } #w3tc #toc ul { - margin: 0; - padding: 0 + margin: 0; + padding: 0 } #w3tc #toc li { - margin: 0; - padding: 0 + margin: 0; + padding: 0 } #w3tc #qa { - clear: both; - padding: 10px; + clear: both; + padding: 10px; } #w3tc #qa, #w3tc #about, #w3tc #install { - width: 760px; + width: 760px; } #w3tc fieldset { - margin: 1em 0; - background: #fdfdfd; - padding: 0 1em; - border: 1px solid #bbb; - border-radius: 11px; - -webkit-border-radius: 11px; - -moz-border-radius: 11px; + margin: 1em 0; + background: #fdfdfd; + padding: 0 1em; + border: 1px solid #bbb; + border-radius: 11px; + -webkit-border-radius: 11px; + -moz-border-radius: 11px; } #w3tc fieldset .submit { - margin: 1em 0 0 0; - padding: 0; + margin: 1em 0 0 0; + padding: 0; } #w3tc fieldset legend { - color: #999; - padding: 0 5px; - font-weight: bold; + color: #999; + padding: 0 5px; + font-weight: bold; } #w3tc fieldset legend a:hover { - text-decoration: none; + text-decoration: none; } #w3tc pre.code { - color: #000; - margin: 1em 0; - overflow: auto; - background: #eee; - padding: 12px 15px; - border: 1px solid #ccc; + color: #000; + margin: 1em 0; + overflow: auto; + background: #eee; + padding: 12px 15px; + border: 1px solid #ccc; } #w3tc pre.console { - color: #ccc; - margin: 1em 0; - overflow: auto; - background: #000; - padding: 12px 15px; - border: 1px solid #ccc; + color: #ccc; + margin: 1em 0; + overflow: auto; + background: #000; + padding: 12px 15px; + border: 1px solid #ccc; } #w3tc-cdn-general th { - width: 400px; + width: 400px; } #cdn_cnames li { - padding: 5px; - cursor: ns-resize; + padding: 5px; + cursor: ns-resize; } #cdn_cnames li:hover { - background: #e9e9e9; + background: #e9e9e9; } #cdn_cnames li span, .w3tc_cdn_cname_comment { - padding-left: 10px; - color: #999; + padding-left: 10px; + color: #999; } #mobile_groups th, #referrer_groups th, +.w3tc_cachegroups th, #mobile_groups td, -#referrer_groups td { - padding: 10px 10px 10px 15px; +#referrer_groups td, +.w3tc_cachegroups td, + { + padding: 10px 10px 10px 15px; } #mobile_groups li, -#referrer_groups li { - cursor: ns-resize; - list-style: none; - background: #f0f0f0; - margin-bottom: 1em; - border-radius: 8px; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; +#referrer_groups li, +.w3tc_cachegroups li { + cursor: ns-resize; + list-style: none; + background: #f0f0f0; + margin-bottom: 1em; + border-radius: 8px; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; } #mobile_groups li:hover, -#referrer_groups li:hover { - background: #e9e9e9; +#referrer_groups li:hover, +.w3tc_cachegroups li:hover { + background: #e9e9e9; } #mobile_groups li table, -#referrer_groups li table { - margin: 0; +#referrer_groups li table, +.w3tc_cachegroups li table { + margin: 0; } .mobile_group, -.referrer_group { - font-weight: bold; +.referrer_group, +.cookiegroup_name { + font-weight: bold; } .w3tc-rules { - display: none; - margin: 1em 0; - padding: 0; - background: none; + display: none; + margin: 1em 0; + padding: 0; + background: none; } .minify-files { - margin-bottom: 1em; + margin-bottom: 1em; } .minify-files li { - padding: 2px; - list-style: none; - background: #f0f0f0; - cursor: ns-resize; - border-radius: 8px; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; - line-height: 100%; + padding: 2px; + list-style: none; + background: #f0f0f0; + cursor: ns-resize; + border-radius: 8px; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + line-height: 100%; } .minify-files li:hover { - background: #e9e9e9; + background: #e9e9e9; } .minify-files th, .minify-files td { - width: auto; - line-height: 100%; - padding: 3px 3px; - font-weight: normal; - text-align: left; + width: auto; + line-height: 100%; + padding: 3px 3px; + font-weight: normal; + text-align: left; } #support_loading, .w3tc-checking { - height: 32px; - background: url(../img/wpspin_light.gif) left center no-repeat; - padding-left: 38px; - line-height: 32px; - margin: 1em 0; - font-weight: bold; + height: 32px; + background: url(../img/wpspin_light.gif) left center no-repeat; + padding-left: 38px; + line-height: 32px; + margin: 1em 0; + font-weight: bold; } #w3tc .postbox .inside { - padding: 0 10px; + padding: 0 10px; } #w3tc .postbox .hndle { - cursor: auto; + cursor: auto; } #pgcache_reject_roles label, #newrelic_accept_roles label, #cdn_reject_roles label { - margin-right: 15px; + margin-right: 15px; } #minify_form fieldset{ - padding-bottom: 10px; + padding-bottom: 10px; } #minify_form .html-tag{ - font-weight: bold; - color: #107e0e; - float: right; + font-weight: bold; + color: #107e0e; + float: right; } #minify_form fieldset label{ - margin-bottom: 5px; - margin-right:10px; + margin-bottom: 5px; + margin-right:10px; } #minify_table td { - padding: 0; + padding: 0; } #minify_table table td { - white-space: nowrap; - padding: 0; + white-space: nowrap; + padding: 0; } #minify_table .placement { - width: 110px; + width: 110px; } #minify_table .options { - padding-left: 10px; + padding-left: 10px; } .recom_js_type { - width:160px; + width:160px; } .w3tc-missing-files ul { - margin-left:5px; + margin-left:5px; } #w3tc .updated p, #w3tc_dashboard p, #w3tc_general p, #w3tc_cdn p { line-height: 30px} @@ -333,105 +340,105 @@ input.w3tc-error, textarea.w3tc-error { #w3tc .updated p, #w3tc_general p, #w3tc_cdn p { line-height: 30px} #w3tc #application ul { - margin-top:0; + margin-top:0; } #monitoring td label{ - display: block; + display: block; } #monitoring td label input { - margin-right: 5px; + margin-right: 5px; } .w3tc-required-changes strong { - font-weight: bold; - font-family: courier, sans-serif; + font-weight: bold; + font-family: courier, sans-serif; } .minify_auto_test, .w3tc-loading { - height: 16px; - background: url(../img/wpspin_light.gif) right top no-repeat; - width: 16px; + height: 16px; + background: url(../img/wpspin_light.gif) right top no-repeat; + width: 16px; } .create-error p { - margin:0; + margin:0; } .create-error { - padding: 3px; - display:none; + padding: 3px; + display:none; } #cdn_netdna_authorization_key, #cdn_maxcdn_authorization_key, .maxcdn-netdna-widget-base .button-secondary{ - margin-bottom: 3px; + margin-bottom: 3px; } #w3tc_extensions table.extensions .column-name { - width:180px; + width:180px; } #wpbody-content .metabox-holder.extension-settings { - padding-top: 0; + padding-top: 0; } #w3tc .subsubsub { - margin-left: 0; + margin-left: 0; } #edge-mode.updated p { - line-height: 20px; + line-height: 20px; } td.w3tc-td-with-button { - line-height: 28px; + line-height: 28px; } .w3tc_loading { - position:absolute; - width: 100%; - text-align: center; + position:absolute; + width: 100%; + text-align: center; } .w3tc_hidden { - visibility: hidden; + visibility: hidden; } .w3tc_none { - display: none; + display: none; } th.w3tc_config_checkbox { - padding-top: 15px; + padding-top: 15px; } td.w3tc_config_value_text { - padding-top: 20px; + padding-top: 20px; } .w3tc_cdn_cnames_readonly { - margin-top: .5em; - margin-right: 20px; - margin-bottom: .5em; + margin-top: .5em; + margin-right: 20px; + margin-bottom: .5em; } .w3tchelp_loading_outer { - width: 100%; - margin-top: 50px; + width: 100%; + margin-top: 50px; } .w3tchelp_loading_inner { - margin-left: auto; - margin-right: auto; + margin-left: auto; + margin-right: auto; } .w3tc_menu_item_pro { - color: #0FA9B5; + color: #0FA9B5; } .w3tc_menu_item_pro:hover { - color: #ddd; + color: #ddd; } .w3tc_popup_form { - padding: 20px; + padding: 20px; } @@ -439,7 +446,7 @@ td.w3tc_config_value_text { /* js bound to id */ th.w3tc_extensions_manage_column_check { - padding: 0 0 0 5px; - vertical-align: middle; - width: 2.2em; + padding: 0 0 0 5px; + vertical-align: middle; + width: 2.2em; } diff --git a/pub/js/lightbox.js b/pub/js/lightbox.js index ed48236..b1d5254 100644 --- a/pub/js/lightbox.js +++ b/pub/js/lightbox.js @@ -6,7 +6,7 @@ var W3tc_Lightbox = { create: function() { var me = this; - this.container = jQuery('
          ').css({ + this.container = jQuery('
          ').css({ top: 0, left: 0, width: 0, @@ -145,8 +145,8 @@ var W3tc_Lightbox = { var v = jQuery(form_selector).find('input').each(function(i) { var name = jQuery(this).attr('name'); var type = jQuery(this).attr('type'); - if (type == 'radio') { - if (!jQuery(this).attr('checked')) + if (type == 'radio' || type == 'checkbox' ) { + if (!jQuery(this).prop('checked')) return; } @@ -467,87 +467,6 @@ function w3tc_lightbox_cdn_s3_bucket_location(type, nonce) { }); } -function w3tc_lightbox_netdna_maxcdn_pull_zone(type, nonce) { - W3tc_Lightbox.open({ - width: 500, - height: 400, - url: 'admin.php?page=w3tc_dashboard&w3tc_cdn_create_netdna_maxcdn_pull_zone_form&type=' + type + '&_wpnonce=' + nonce, - callback: function(lightbox) { - jQuery('#create_pull_zone', lightbox.container).click(function() { - var loader = jQuery('#pull-zone-loading'); - loader.addClass('w3tc-loading'); - var pull_button = jQuery(this); - pull_button.attr("disabled", "disabled"); - jQuery('.create-error').text(''); - var name_val = jQuery('#name', lightbox.container).val(); - var name_filter = /^[a-zA-Z\d\-]*$/; - if (name_val == '') { - jQuery('#name', lightbox.container).addClass('w3tc-error'); - jQuery('.name_message', lightbox.container).text('Cannot be empty.'); - } else if(name_val.length < 3) { - jQuery('#name', lightbox.container).addClass('w3tc-error'); - jQuery('.name_message', lightbox.container).text('Too short.'); - } else if (name_val.length > 32) { - jQuery('#name', lightbox.container).addClass('w3tc-error'); - jQuery('.name_message', lightbox.container).text('Too long.'); - } else if (!name_filter.test(name_val)) { - jQuery('#name', lightbox.container).addClass('w3tc-error'); - jQuery('.name_message', lightbox.container).text('Cannot use unsupported characters.'); - } else { - jQuery('#name', lightbox.container).removeClass('w3tc-error'); - jQuery('.name_message', lightbox.container).text(''); - } - - var label_val = jQuery('#label', lightbox.container).val(); - if (label_val == '') { - jQuery('#label', lightbox.container).addClass('w3tc-error'); - jQuery('.label_message', lightbox.container).text('Cannot be empty.'); - } else if(label_val.length < 1) { - jQuery('#label', lightbox.container).addClass('w3tc-error'); - jQuery('.label_message', lightbox.container).text('Too short.'); - } else if (label_val.length > 255) { - jQuery('#label', lightbox.container).addClass('w3tc-error'); - jQuery('.label_message', lightbox.container).text('Too long.'); - } else { - jQuery('#label', lightbox.container).removeClass('w3tc-error'); - jQuery('.label_message', lightbox.container).text(''); - } - if (!jQuery('#label').hasClass('w3tc-error') && !jQuery('#name').hasClass('w3tc-error')) { - jQuery.post('admin.php?page=w3tc_dashboard&w3tc_cdn_create_netdna_maxcdn_pull_zone', {name:name_val, label: label_val, nonce: jQuery('#_wp_nonce').val(), type: type},function(data) { - loader.removeClass('w3tc-loading'); - if (data['status'] == 'error') { - jQuery('.create-error').show(); - jQuery('.create-error').html('

          Something is wrong:
          ' + data['message'] + '

          '); - pull_button.removeAttr("disabled"); - } else { - if (jQuery('#cdn_cnames > :first-child > :first-child').val() == '') { - jQuery('#cdn_cnames > :first-child > :first-child').val(data['temporary_url']); - jQuery('.netdna-maxcdn-form').html('

          Pull zone was successfully created. Following url was added as default "Replace site\'s hostname with:" ' - + data['temporary_url'] - + '

          ' - + '

          ' - ); - } else { - jQuery('.netdna-maxcdn-form').html('

          Pull zone was successfully created. cnames were already set so "Replace site\'s hostname with:" were not replaced with ' - + data['temporary_url'] - + '

          ' - + '

          ' - ); - } - } - }, - 'json'); - } else { - loader.removeClass('w3tc-loading'); - pull_button.removeAttr("disabled"); - } - }); - jQuery('.button', lightbox.container).click(function() { - lightbox.close(); - }); - } - }); -} jQuery(function() { jQuery('.button-minify-recommendations').click(function() { @@ -582,13 +501,6 @@ jQuery(function() { return false; }); - jQuery('#netdna-maxcdn-create-pull-zone').click(function() { - var type = jQuery(this).metadata().type; - var nonce = jQuery(this).metadata().nonce; - w3tc_lightbox_netdna_maxcdn_pull_zone(type, nonce); - return false; - }); - jQuery('body').on('click', '.w3tc_lightbox_close', function() { W3tc_Lightbox.close(); }); diff --git a/pub/js/options.js b/pub/js/options.js index 15ebdd8..251d462 100644 --- a/pub/js/options.js +++ b/pub/js/options.js @@ -260,132 +260,6 @@ function w3tc_beforeunload() { return 'Navigate away from this page without saving your changes?'; } -/** - * - * @param type - * @param nonce - */ -function w3tc_validate_cdn_key_result(type, nonce) { - var key = jQuery('#cdn_' + type + '_authorization_key').val(); - jQuery('#cdn_result_message').text(''); - var result = jQuery('#validate_cdn_key_result'); - if (key.length == 0) { - result.html('').removeClass('w3tc-error').removeClass('w3tc-success').removeClass('w3tc-checking'); - return; - } - result.html('Validating ...').addClass('w3tc-checking'); - - if (key.split('+').length!=3) { - result.removeClass('w3tc-checking'); - result.html('Key is invalid').addClass('w3tc-error'); - return; - } - var params = { - w3tc_cdn_validate_authorization_key: 1, - type: type, - authorization_key: key, - _wpnonce: nonce - }; - jQuery('#validate_cdn_key').prop('disabled', true); - jQuery.post('admin.php?page=w3tc_dashboard', params, function(data) { - result.html('').removeClass('w3tc-error').removeClass('w3tc-success').removeClass('w3tc-checking'); - var element; - if (data.result == 'create') { - jQuery('#create_zone_area').show(); - element = jQuery('#normal-sortables'); - if (element.length) - element.masonry('reload'); - } else if (data.result == 'single') { - var message = data.cnames.join('
          '); - jQuery('#cdn_result_message').html('Preexisting zone has been selected and saved for the CDN engine. Hostnames used:
          ' + message); - jQuery('#cdn_cnames > :first-child > :first-child').val(data.cnames.shift()); - for (var i in data.cnames) { - jQuery('#cdn_cnames').append('
        1. '); - w3tc_cdn_cnames_assign(); - } - } else if (data.result == 'many') { - jQuery('#select_pull_zone').show(); - var mySelect = jQuery('#cdn_' + type +'_zone_id'); - mySelect.empty(); - jQuery.each(data.zones, function(val, zone) { - mySelect.append( - jQuery('').val(zone.id).html(zone.name) - ); - }); - if (data.data.id) { - jQuery("#cdn_maxcdn_zone_id").val(data.data.id); - var message = data.data.cnames.join('
          '); - jQuery('#cdn_result_message').html('Preexisting zone has been selected and saved for the CDN engine. Hostnames used:
          ' + message); - jQuery('#cdn_cnames > :first-child > :first-child').val(data.data.cnames.shift()); - for (var x in data.data.cnames) { - jQuery('#cdn_cnames').append('
        2. '); - w3tc_cdn_cnames_assign(); - } - } - element = jQuery('#normal-sortables'); - if (element.length) - element.masonry('reload'); - } - result.removeClass('w3tc-checking'); - if (data.result != 'error' && data.result != 'notsupported') - result.html('Key is valid').addClass('w3tc-success'); - else - result.html(data.message).addClass('w3tc-error'); - jQuery('#validate_cdn_key').prop('disabled', false); - }, 'json'); -} - -function w3tc_create_zone(type, nonce) { - var params = { - w3tc_cdn_auto_create_netdna_maxcdn_pull_zone: 1, - type: type, - authorization_key: jQuery('#cdn_' + type + '_authorization_key').val(), - _wpnonce: nonce - }; - var result = jQuery('#create_pull_zone_result'); - result.html('').removeClass('w3tc-error').removeClass('w3tc-success').removeClass('w3tc-checking'); - jQuery("#create_default_zone").prop('disabled', true); - result.html('Creating ... ').addClass('w3tc-checking'); - - jQuery.post('admin.php?page=w3tc_dashboard', params, function(data) { - if (data.cnames && data.cnames.length) { - jQuery('#cdn_cnames > :first-child > :first-child').val(data.cnames.shift()); - for (var i in data.cnames) { - jQuery('#cdn_cnames').append('
        3. '); - w3tc_cdn_cnames_assign(); - } - result.text('Created ').removeClass('w3tc-checking').addClass('w3tc-success'); - } else { - result.text(data.message).removeClass('w3tc-checking').addClass('w3tc-error'); - jQuery("#create_default_zone").prop('disabled', false); - - } - }, 'json'); -} - -function w3tc_use_poll_zone(type, nonce) { - var zone_id = jQuery("#cdn_" + type +"_zone_id").val(); - var params = { - w3tc_cdn_use_netdna_maxcdn_pull_zone: 1, - type: type, - zone_id: zone_id, - authorization_key: jQuery('#cdn_' + type + '_authorization_key').val(), - _wpnonce: nonce - }; - jQuery.post('admin.php?page=w3tc_dashboard', params, function(data) { - if (data.result == 'valid') { - var message = data.cnames.join('
          '); - jQuery('#cdn_result_message').html('Zone has been selected and saved for the CDN engine. Hostnames used:
          ' + message); - jQuery('#cdn_cnames > :first-child > :first-child').val(data.cnames.shift()); - for (var i in data.cnames) { - jQuery('#cdn_cnames').append('
        4. '); - w3tc_cdn_cnames_assign(); - } - } else { - alert(data.message); - } - }, 'json'); -} function w3tc_starts_with(s, starts_with) { s = s.replace(/\n/g, ''); @@ -397,8 +271,6 @@ function w3tc_starts_with(s, starts_with) { jQuery(function() { // general page - w3tc_toggle('enabled'); - jQuery('.w3tc_read_technical_info').click(function() { jQuery('.w3tc_technical_info').toggle(); }); @@ -727,7 +599,8 @@ jQuery(function() { engine: 's3', 'config[key]': jQuery('#cdn_s3_key').val(), 'config[secret]': jQuery('#cdn_s3_secret').val(), - 'config[bucket]': jQuery('#cdn_s3_bucket').val() + 'config[bucket]': jQuery('#cdn_s3_bucket').val(), + 'config[bucket_location]': jQuery('#cdn_s3_bucket_location').val() }); if (cnames.length) { @@ -741,6 +614,7 @@ jQuery(function() { 'config[key]': jQuery('#cdn_cf_key').val(), 'config[secret]': jQuery('#cdn_cf_secret').val(), 'config[bucket]': jQuery('#cdn_cf_bucket').val(), + 'config[bucket_location]': jQuery('#cdn_cf_bucket_location').val(), 'config[id]': jQuery('#cdn_cf_id').val() }); @@ -801,29 +675,6 @@ jQuery(function() { } break; - case 'maxcdn': - jQuery.extend(params, { - engine: 'maxcdn', - 'config[authorization_key]': jQuery('#cdn_maxcdn_authorization_key').val(), - 'config[zone_id]': jQuery('#cdn_maxcdn_zone_id').val() - }); - - if (cnames.length) { - params['config[domain][]'] = cnames; - } - break; - case 'netdna': - jQuery.extend(params, { - engine: 'netdna', - 'config[authorization_key]': jQuery('#cdn_netdna_authorization_key').val(), - 'config[zone_id]': jQuery('#cdn_netdna_zone_id').val() - }); - - if (cnames.length) { - params['config[domain][]'] = cnames; - } - break; - case 'cotendo': var zones = [], zones_val = jQuery('#cdn_cotendo_zones').val(); @@ -901,6 +752,12 @@ jQuery(function() { status.removeClass('w3tc-error'); status.removeClass('w3tc-success'); status.addClass('w3tc-process'); + + var status2 = jQuery('#cdn_create_container_status'); + status2.removeClass('w3tc-error'); + status2.removeClass('w3tc-success'); + status2.html(''); + status.html('Testing...'); jQuery.post('admin.php?page=w3tc_dashboard', params, function(data) { @@ -1003,6 +860,12 @@ jQuery(function() { status.removeClass('w3tc-error'); status.removeClass('w3tc-success'); status.addClass('w3tc-process'); + + var status2 = jQuery('#cdn_test_status'); + status2.removeClass('w3tc-error'); + status2.removeClass('w3tc-success'); + status2.html(''); + status.html('Creating...'); jQuery.post('admin.php?page=w3tc_dashboard', params, function(data) { @@ -1047,6 +910,8 @@ jQuery(function() { jQuery.post('admin.php?page=w3tc_dashboard', { w3tc_test_redis: 1, servers: jQuery('#redis_servers').val(), + dbid : jQuery('#redis_dbid').val(), + password : jQuery('#redis_password').val(), _wpnonce: jQuery(this).metadata().nonce }, function(data) { status.addClass(data.result ? 'w3tc-success' : 'w3tc-error'); @@ -1146,134 +1011,6 @@ jQuery(function() { return ret; }); - // support tabs - jQuery('#support_more_files').live('click', function() { - jQuery(this).before('
          '); - - return false; - }); - - jQuery('#support_form').live('submit', function() { - var url = jQuery('.required #support_url'); - var name = jQuery('.required #support_name'); - var email = jQuery('.required #support_email'); - var phone = jQuery('.required #support_phone'); - var subject = jQuery('.required #support_subject'); - var description = jQuery('.required #support_description'); - var wp_login = jQuery('.required #support_wp_login'); - var wp_password = jQuery('.required #support_wp_password'); - var ftp_host = jQuery('.required #support_ftp_host'); - var ftp_login = jQuery('.required #support_ftp_login'); - var ftp_password = jQuery('.required #support_ftp_password'); - - if (url.size() && url.val() == '') { - alert('Please enter the address of your site in the Site URL field.'); - url.focus(); - return false; - } - - if (name.size() && name.val() == '') { - alert('Please enter your name in the Name field.'); - name.focus(); - return false; - } - - if (email.size() && !/^[a-z0-9_\-\.]+@[a-z0-9-\.]+\.[a-z]{2,5}$/.test(email.val().toLowerCase())) { - alert('Please enter valid email address in the E-Mail field.'); - email.focus(); - return false; - } - - if (phone.size() && !/^[0-9\-\. \(\)\+]+$/.test(phone.val())) { - alert('Please enter your phone in the phone field.'); - phone.focus(); - return false; - } - - if (subject.size() && subject.val() == '') { - alert('Please enter subject in the subject field.'); - subject.focus(); - return false; - } - - if (description.size() && description.val() == '') { - alert('Please describe the issue in the issue description field.'); - description.focus(); - return false; - } - - if (wp_login.size() && wp_login.val() == '') { - alert('Please enter an administrator login. Remember you can create a temporary one just for this support case.'); - wp_login.focus(); - return false; - } - - if (wp_password.size() && wp_password.val() == '') { - alert('Please enter WP Admin password, be sure it\'s spelled correctly.'); - wp_password.focus(); - return false; - } - - if (ftp_host.size() && ftp_host.val() == '') { - alert('Please enter SSH or FTP host for your site.'); - ftp_host.focus(); - return false; - } - - if (ftp_login.size() && ftp_login.val() == '') { - alert('Please enter SSH or FTP login for your server. Remember you can create a temporary one just for this support case.'); - ftp_login.focus(); - return false; - } - - if (ftp_password.size() && ftp_password.val() == '') { - alert('Please enter SSH or FTP password for your FTP account.'); - ftp_password.focus(); - return false; - } - - return true; - }); - - jQuery('#support_request_type').live('change', function() { - var request_type = jQuery(this); - - if (request_type.val() == '') { - alert('Please select request type.'); - request_type.focus(); - - return false; - } - - var type = request_type.val(), action = ''; - - switch (type) { - case 'bug_report': - case 'new_feature': - action = 'support_form'; - break; - - case 'email_support': - case 'phone_support': - case 'plugin_config': - case 'theme_config': - case 'linux_config': - action = 'support_payment'; - break; - } - - if (action) { - jQuery('#support_container').html('
          Loading...
          ').load('admin.php?page=w3tc_support&w3tc_' + action + '&request_type=' + type + '&_wpnonce=' + request_type.metadata().nonce); - - return false; - } - - return true; - }); - - jQuery('#support_cancel').live('click', function() { - jQuery('#support_container').html('
          Loading...
          ').load('admin.php?page=w3tc_support&w3tc_support_select&_wpnonce=' + jQuery(this).metadata().nonce); - }); // mobile tab jQuery('#mobile_form').submit(function() { diff --git a/pub/opcache.php b/pub/opcache.php index df41477..f5edbf6 100644 --- a/pub/opcache.php +++ b/pub/opcache.php @@ -1,6 +1,6 @@ flush_all(); + $o->flush_all( $extras ); } /** * Purges/Flushes post page */ -function w3tc_flush_post( $post_id ) { +function w3tc_flush_post( $post_id, $extras = null ) { $o = \W3TC\Dispatcher::component( 'CacheFlush' ); - $o->flush_post( $post_id ); + $o->flush_post( $post_id, $extras ); } /** * Purges/Flushes all posts */ -function w3tc_flush_posts() { +function w3tc_flush_posts( $extras = null ) { $o = \W3TC\Dispatcher::component( 'CacheFlush' ); - $o->flush_posts(); + $o->flush_posts( $extras ); } /** * Purges/Flushes url */ -function w3tc_flush_url( $url ) { +function w3tc_flush_url( $url, $extras = null ) { $o = \W3TC\Dispatcher::component( 'CacheFlush' ); - $o->flush_url( $url ); + $o->flush_url( $url, $extras ); } @@ -532,7 +537,7 @@ function w3tc_opcache_flush_file( $file, $http = false ) { * Deprecated. Retained for 3rd parties that used it. see w3tc_config() * * Some plugins make incorrect decisions based on configuration - * and force to disable modules working otherwise or + * and force to disable modules working otherwise or * adds notices on each wp-admin page without ability to remove it. * By defining W3TC_CONFIG_HIDE you may still use w3tc configuration you like. */ @@ -540,11 +545,11 @@ function w3tc_opcache_flush_file( $file, $http = false ) { class W3_Config { public function __construct( $master = false, $blog_id = null ) { } - + public function get_string( $key, $default = '', $trim = true ) { return ''; } - + public function get_integer( $key, $default = 0 ) { return 0; } diff --git a/w3-total-cache.php b/w3-total-cache.php index 902390c..ca101cb 100644 --- a/w3-total-cache.php +++ b/w3-total-cache.php @@ -2,7 +2,7 @@ /* Plugin Name: W3 Total Cache Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress. -Version: 0.9.5.4 +Version: 0.9.6 Plugin URI: https://www.w3-edge.com/wordpress-plugins/w3-total-cache/ Author: Frederick Townes Author URI: http://www.linkedin.com/in/fredericktownes
      +
      + +
      +
      + +
      +
      + +
      +
      + +
      @@ -400,12 +436,12 @@
      - checkbox( 'pgcache.cache.nginx_handle_xml', true ) ?>
      - + checkbox( 'pgcache.cache.nginx_handle_xml' ) ?>
      +