diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index dd087ee1..e893c62e 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -11,7 +11,6 @@ jobs: fail-fast: false matrix: php-version: - - "5.4" - "7.4" - "8.0" - "8.1" @@ -20,7 +19,7 @@ jobs: name: Lint PHP ${{ matrix.php-version }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP version uses: shivammathur/setup-php@v2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9911e779..089b211a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,22 @@ -Welcome! Thanks for your interest in contributing to jquery-wp-content. You're **almost** in the right place. More information on how to contribute to this and all other jQuery Foundation projects is over at [contribute.jquery.org](https://contribute.jquery.org). You'll definitely want to take a look at the articles on contributing [to our websites](https://contribute.jquery.org/web-sites/) and [code](https://contribute.jquery.org/code). +# Contributing + +Welcome! Thanks for your interest in contributing to jquery-wp-content. More information on how to contribute to this and other projects is over at [contribute.jquery.org](https://contribute.jquery.org). You'll definitely want to take a look at the articles on contributing [to our websites](https://contribute.jquery.org/web-sites/) and [code](https://contribute.jquery.org/code). You may also want to take a look at our [commit & pull request guide](https://contribute.jquery.org/commits-and-pull-requests/) and [style guides](https://contribute.jquery.org/style-guide/) for instructions on how to maintain your fork and submit your code. Before we can merge any pull request, we'll also need you to sign our [contributor license agreement](https://contribute.jquery.org/cla). You can [Chat on Gitter](https://gitter.im/jquery/dev), should you have any questions. If you've never contributed to open source before, we've put together [a short guide with tips, tricks, and ideas on getting started](https://contribute.jquery.org/open-source/). + +## Code knowledge + +### Protocol-relative URLs + +As of 2023, we run with the default WordPress settings to formatting and cleaning URLs. If revisiting this in the future, consider the following constraints: + +* When accessing sites in older browsers over HTTP instead of HTTPS, references to theme assets (e.g. stylesheets) must either use the current scheme, or use a protocol-relative URL, or be an absolute path URL without protocol or hostname (`theme_root_uri`). + +* Intra-site links to pages and categories should generally use a path or the canonical URL. + +* Avoid stripping the protocol from a `clean_url` filter as various uses require a full URL: + * Server-side requests, such as for `downloads.wordpress.org`, must specify an explicit protocol in the URL. + * When building `/wp-sitemap.xml`, URLs must be full and with the canonical protocol explicitly set. Sitemaps are invalid if they contain relative URLs. + * When outputting `` via `wp_head/rel_canonical`, the URL must be full and canonical. Or `rel_canonical` must be remove_action'ed replaced with a custom version that calls `esc_attr()` instead of `esc_url()` to avoid the `clean_url` filter. diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index ccae0b52..00000000 --- a/INSTALL.md +++ /dev/null @@ -1,119 +0,0 @@ -# jquery-wp-content - -`jquery-wp-content` is a **custom replacement** for the `wp-content` directory which contains the plugins, themes and site configuration to run the jQuery multi-site WordPress network. - -## Warning - -Configuring your own services can result in different configurations from the production environment. It is strongly encouraged to use the virtual machine environment as described in [README.md](README.md). - -## Prerequisites - -This install guide assumes you have certain prerequisites already configured within your environment. - -* Apache 2.4+ -* MySQL 5.7+ or MariaDB 10+ -* PHP 7.4+ - -## Installation - -1. Configure your local webserver with a virtual host that covers the relevant jQuery domains, such as `*.jquery.com` and `*.jqueryui.com`, all pointing to the same root. For example, in Apache: - -``` - - ServerName local.jquery.com - ServerAlias *.jquery.com *.jqueryui.com *.jquery.org *.jquerymobile.com - DocumentRoot "/srv/www/jquery" - php_value memory_limit 256M - - Options All - AllowOverride All - Order allow,deny - Allow from all - - -``` - -Make sure that virtual hosts are enabled as well: - -``` -NameVirtualHost *:80 -``` - -Both blocks of code should be pasted into `extra/httpd-vhosts.conf`. -Be sure to check `httpd.conf` to verify there is a line that includes -`httpd-vhosts.conf`. It may already exist, but be commented out. - -Check `httpd.conf` to ensure that the PHP module is enabled as well. - -You do not need to configure your `/etc/hosts` file for `local.*` because `jquery.com`'s DNS handles this for you. However, if you plan to work offline, you can use the following rules: - -``` -127.0.0.1 local.jquery.com local.api.jquery.com local.blog.jquery.com local.releases.jquery.com local.learn.jquery.com local.plugins.jquery.com -127.0.0.1 local.jqueryui.com local.api.jqueryui.com local.blog.jqueryui.com -127.0.0.1 local.jquerymobile.com local.api.jquerymobile.com local.blog.jquerymobile.com -127.0.0.1 local.jquery.org local.brand.jquery.org local.contribute.jquery.org local.meetings.jquery.org -``` - -1. Place the WordPress core files **at** the document root you chose. For example, if you used `/srv/www/jquery`, you should unzip or clone WordPress directly into that directory, *not* a directory below it. **Do not install WordPress.** You can do this any number of ways: - * Download the latest version from https://wordpress.org/latest.zip - * Check out the latest tag from https://core.svn.wordpress.org/tags/ - * Clone the official WordPress Github mirror at https://github.com/WordPress/WordPress - -1. Clone `jquery-wp-content` inside of the directory where you put WordPress, so you have a file tree that looks like this: - -``` -├── jquery -│   ├── gw-resources -│   ├── index.php -│   ├── jquery-wp-content -│   ├── license.txt -│   ├── readme.html -│   ├── wp-activate.php -│   ├── wp-admin -│   ├── wp-blog-header.php -│   ├── wp-comments-post.php -│   ├── wp-config-sample.php -│   ├── wp-content -│   ├── ... -│   └── xmlrpc.php -``` - -1. Create a MySQL database and user. You can choose any name you want for both. Follow the [WordPress instructions](https://codex.wordpress.org/Installing_WordPress#Step_2:_Create_the_Database_and_a_User) for a step by step guide. - -1. Copy `jquery-wp-content/wp-config-sample.php` and move it up one directory, to `wp-config.php`. Fill in your database credentials. - -1. Create an .htaccess file with the following content into that same document root: - -``` -RewriteEngine On -RewriteBase / -RewriteRule ^index\.php$ - [L] - -RewriteRule ^resources/?$ index.php [L] -RewriteRule ^resources/(.+) gw-resources/%{HTTP_HOST}/$1 [L] - -# Add a trailing slash to the wp-admin of a subsite. -RewriteRule ^([_0-9a-zA-Z\.-]+/)?wp-admin$ $1wp-admin/ [R=301,L] - -RewriteCond %{REQUEST_FILENAME} -f [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^ - [L] - -# Handle wp-admin, wp-includes, and root PHP files for subsites. -RewriteRule ^[_0-9a-zA-Z\.-]+/((wp-admin|wp-includes).*) $1 [L] -RewriteRule ^[_0-9a-zA-Z\.-]+/(.*\.php)$ $1 [L] - -RewriteRule . index.php [L] -``` - -1. Make sure that you have assigned your WordPress files and directories the correct permissions. -For example, if your WordPress files are in the directory ```wordpress```, and you are running Apache under Mac OS X with the ```_www``` user: - -``` -sudo chown -R _www wordpress -sudo chmod -R g+w wordpress -``` - -1. Restart your web server so the changes above are in use. - -1. Go to `http://local.jquery.com` and walk through the standard WordPress installation. `jquery-wp-content` includes a special install script that will initialize the entire network. diff --git a/NOTICE.txt b/NOTICE.txt index 4653ece0..d78dca0d 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -16,18 +16,37 @@ with this program; if not, write to the Free Software Foundation, Inc., ==== +- Cairo + https://github.com/Gue3bara/Cairo + located at themes/jquery/lib/Cairo + - Disable Emojis https://wordpress.org/plugins/disable-emojis/ - located at plugins/disable-emojis + located at plugins/disable-emojis + +- Font Awesome + https://fontawesome.com/v3/ + located at themes/jquery/lib/FontAwesome + and included in themes/jquery/css/base.css - Gilded Wordpress https://github.com/scottgonzalez/grunt-wordpress - located at plugins/gilded-wordpress + located at plugins/gilded-wordpress + +- Memcached + https://wordpress.org/plugins/memcached/ + https://github.com/Automattic/wp-memcached/tree/35e1ea16f6b8cb8a1e6fbca124e33a44db21fa74 (2023-12-11) + located at plugins/memcached/ - No Comments on Pages https://wordpress.org/plugins/no-comments-on-pages/ - located at plugins/no-comments-on-pages + located at plugins/no-comments-on-pages + +- Normalize.css + https://github.com/necolas/normalize.css/tree/v1.0.1 + located at themes/jquery/lib/normalize + and included in themes/jquery/css/base.css -- TinyNav.js - http://tinynav.com/ - located at themes/jquery/js/plugins.js +- typesense-minibar + https://github.com/jquery/typesense-minibar + located at themes/jquery/lib/typesense-minibar diff --git a/composer.json b/composer.json index 16947f0e..1da74c8f 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,21 @@ { "require": { - "php": ">= 5.4" + "php": ">= 7.4" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "1.3.2" }, "scripts": { - "test": "parallel-lint --exclude vendor/composer/autoload_static.php ." + "test": "parallel-lint --exclude vendor/ .", + "deps": [ + "curl -O -q --output-dir themes/jquery/lib/typesense-minibar 'https://raw.githubusercontent.com/jquery/typesense-minibar/1.3.4/{typesense-minibar.css,typesense-minibar.js,LICENSE.txt}'", + "curl -q https://raw.githubusercontent.com/jquery/typesense-minibar/1.3.4/typesense-minibar-foot.css >> themes/jquery/lib/typesense-minibar/typesense-minibar.css", + + "curl -O -q --output-dir plugins/memcached 'https://raw.githubusercontent.com/Automattic/wp-memcached/35e1ea16f6b8cb8a1e6fbca124e33a44db21fa74/{object-cache.php,readme.txt,LICENSE}'", + + "git rm -rf plugins/disable-emojis/", + "curl -s 'https://downloads.wordpress.org/plugin/disable-emojis.1.7.7.zip' | tar -xv -C plugins/", + "git add plugins/disable-emojis/" + ] } } diff --git a/config.php b/config.php deleted file mode 100644 index 25da9b45..00000000 --- a/config.php +++ /dev/null @@ -1,68 +0,0 @@ - jquery.com -$live_site = preg_replace( '/:\d+$/', '', strtolower( $_SERVER['HTTP_HOST'] ) ); -if ( JQUERY_STAGING ) { - $live_site = strtr( $live_site, [ JQUERY_STAGING_PREFIX => '' ] ); -} -if ( !isset( $sites[ $live_site ] ) ) { - header( "400 Invalid Request" ); - header( "Content-Type: text/plain" ); - die( 'Domain not served here.' ); -} -if ( isset( $sites[ $live_site ]['subsites'] ) ) { - list( $first_path_segment ) = explode( '/', trim( $_SERVER['REQUEST_URI'], '/' ), 2 ); - if ( $first_path_segment && isset( $sites[ $live_site . '/' . $first_path_segment ] ) ) - $live_site .= '/' . $first_path_segment; -} -define( 'JQUERY_LIVE_SITE', $live_site ); -unset( $live_site, $first_path_segment ); - -// jQuery.com Multisite and domain staging configuration -global $blog_id; -$blog_id = $sites[ JQUERY_LIVE_SITE ]['blog_id']; - -if ( defined( 'MULTISITE' ) && !MULTISITE ) { - die( "Remove define( 'MULTISITE', false ); from wp-config.php. Maybe check out jquery-wp-content/wp-config-sample.php for the current sample." ); -} -define( 'COOKIE_DOMAIN', $sites[ JQUERY_LIVE_SITE ]['cookie_domain'] ); -define( 'MULTISITE', true ); -define( 'SUNRISE', true ); -define( 'SUBDOMAIN_INSTALL', true ); -define( 'DOMAIN_CURRENT_SITE', JQUERY_STAGING_PREFIX . 'jquery.com' ); -define( 'PATH_CURRENT_SITE', '/' ); -define( 'SITE_ID_CURRENT_SITE', 1 ); -define( 'BLOG_ID_CURRENT_SITE', 1 ); -define( 'ADMIN_COOKIE_PATH', '/' ); diff --git a/install.php b/install.php deleted file mode 100644 index f11b0b52..00000000 --- a/install.php +++ /dev/null @@ -1,121 +0,0 @@ -set_role( 'administrator' ); - - $guess_url = wp_guess_url(); - - foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) - $wpdb->$table = $prefixed_table; - - install_network(); - populate_network( 1, $domain, $user_email, 'jQuery Network', $base, false ); - - delete_site_option( 'site_admins' ); - add_site_option( 'site_admins', array( $user->user_login ) ); - - update_site_option( 'allowedthemes', array() ); - - $wpdb->insert( $wpdb->blogs, array( 'site_id' => 1, 'domain' => $domain, 'path' => $base, 'registered' => current_time( 'mysql' ) ) ); - $blog_id = $wpdb->insert_id; - update_user_meta( $user_id, 'source_domain', $domain ); - update_user_meta( $user_id, 'primary_blog', $blog_id ); - if ( !$upload_path = get_option( 'upload_path' ) ) { - $upload_path = substr( WP_CONTENT_DIR, strlen( ABSPATH ) ) . '/uploads'; - update_option( 'upload_path', $upload_path ); - } - update_option( 'fileupload_url', get_option( 'siteurl' ) . '/' . $upload_path ); - - foreach ( jquery_sites() as $site => $details ) - jquery_install_site( $site, $user ); - - wp_new_blog_notification( $blog_title, $guess_url, $user_id, $message = __( 'The password you chose during the install.' ) ); - wp_cache_flush(); - - return array( 'url' => $guess_url, 'user_id' => $user_id, 'password' => $user_password, 'password_message' => $message ); -} - -function jquery_install_site( $site, $user ) { - $sites = jquery_sites(); - $details = $sites[ $site ]; - - if ( strpos( $site, '/' ) ) { - list( $domain, $path ) = explode( '/', $site, 2 ); - $path = '/' . trim( $path, '/' ) . '/'; - } else { - $domain = $site; - $path = '/'; - } - - $default_options = jquery_default_site_options(); - $default_options['admin_email'] = $user->user_email; - - if ( 1 !== $details['blog_id'] ) { - // krinkle(2021-09-03): This used to use insert_blog which didn't take a blog_id. - // Thus, this only worked reliably when setting up a fresh server, or when adding - // sites in the exact order that they are defined, and without any gaps, as otherwise - // the "next" inserted ID would not match what we declare in jquery_sites() - // - // $blog_id = insert_blog( JQUERY_STAGING_PREFIX . $domain, $path, 1 ); - // - // WordPress 5.1, deprecates insert_blog() in favour of a new wp_insert_site() function, - // which does accept a custom 'blog_id' to be set up front. - // But, we are still on WordPress 4.x, so, for now inline what insert_blog() did, but - // augmented with a custom blog_id. - // - // Start insert_blog() - global $wpdb; - $path = trailingslashit($path); - // WordPress 4 terms: The network is a site, and each domain is a blog. - // WordPress 5+ terms: The network is a network, and each domain is a site. - // Network id must be constant for all blogs, always 1. - $site_id = 1; - $result = $wpdb->insert( $wpdb->blogs, array('site_id' => (int)$site_id, 'blog_id' => (int)$details['blog_id'], 'domain' => $domain, 'path' => $path, 'registered' => current_time('mysql')) ); - $blog_id = $result ? $wpdb->insert_id : false; - refresh_blog_details( $blog_id ); - wp_maybe_update_network_site_counts(); - // End insert_blog() - - if ( $blog_id != $details['blog_id'] ) - wp_die( "Something went very wrong when trying to install $domain as site $blog_id-{$details['blog_id']}. Find nacin." ); - - switch_to_blog( $blog_id ); - - install_blog( $blog_id, $details['options']['blogname'] ); - - add_user_to_blog( $blog_id, $user->ID, 'administrator' ); - } - - $options = array_merge( $default_options, $details['options'] ); - foreach ( $options as $option => $value ) - update_option( $option, $value ); - - delete_option( 'rewrite_rules' ); - restore_current_blog(); -} diff --git a/mu-plugins/_loader.php b/mu-plugins/_loader.php index 7215e437..809d1240 100644 --- a/mu-plugins/_loader.php +++ b/mu-plugins/_loader.php @@ -1,6 +1,6 @@ $url ) { + if ( strpos( $url, $emoji_svg_url_bit ) !== false ) { + unset( $urls[$key] ); + } + } + } return $urls; diff --git a/plugins/disable-emojis/readme.txt b/plugins/disable-emojis/readme.txt index 5d3752b0..d3dd881d 100644 --- a/plugins/disable-emojis/readme.txt +++ b/plugins/disable-emojis/readme.txt @@ -1,21 +1,25 @@ -=== Disable Emojis === +=== Disable Emojis (GDPR friendly) === Contributors: ryanhellyer -Tags: emojis +Tags: emojis, gdpr, disable Donate link: https://geek.hellyer.kiwi/donate/ -Requires at least: 4.2 -Tested up to: 4.7 -Stable tag: 1.5.2 +Requires at least: 4.8 +Tested up to: 6.8 +Stable tag: 1.7.7 -This plugin disables the new WordPress emoji functionality. +This plugin disables the new WordPress emoji functionality. GDPR friendly. == Description == -This plugin disables the new WordPress emoji functionality. +This plugin disables the new WordPress emoji functionality. GDPR friendly. -Note: Emoticons will still work and emoji's will still work in browsers which have built in support for them. This plugin simply removes the extra code bloat used to add support for emoji's in older browswers. +Note: Emoticons will still work and emojis will still work in browsers which have built in support for them. This plugin simply removes the extra code bloat used to add support for emojis in older browsers. + += GDPR compliancy = + +This plugin does not do anything to make your site less GDPR compliant. It disables the DNS prefetching of emojis within WordPress, which should ensure improved privacy. To determine if your site is GDPR compliant, please seek legal advice. I have done my best to ensure the plugin is 100% GDPR compliant, but I am not a lawyer so can not guarantee anything ;) == Installation == @@ -31,6 +35,38 @@ Visit the Disable Em == Changelog == += 1.7.7 = +* Confirmed support for newer WordPress versions. + += 1.7.6 = +* Confirmed support for newer WordPress versions. + += 1.7.5 = +* Added Composer support. + += 1.7.4 = +* Fixing typos. + += 1.7.3 = +* Unneeded version bump to shut the WordPress.org notice up. + += 1.7.2 = +* Subtle improvement to code cleanliness. +* Improved documentation regarding GDPR issues. + += 1.7.1 = +* Added GDPR friendly label on advice from Ipstenu. + += 1.7 = +* Removed DNS prefetch URL again. +* This time using simple string check rather than relying on internal WordPress filters. + += 1.6 = +* Removed DNS prefetch URL. Props to Aaron Queen for assisting with this. + += 1.5.3 = +* Catering to new DNS prefetch URL in version 4.7 of core + = 1.5.2 = * Improved documentation. * Removed redundant dns prefetching. Thanks to Milan Dinic for the pull request. diff --git a/plugins/gilded-wordpress/gilded-wordpress.php b/plugins/gilded-wordpress/gilded-wordpress.php index 6f0a8ccb..7a8ba247 100644 --- a/plugins/gilded-wordpress/gilded-wordpress.php +++ b/plugins/gilded-wordpress/gilded-wordpress.php @@ -4,7 +4,7 @@ * Description: Adds custom XML-RPC methods for use with Gilded WordPress. */ -define( 'GW_VERSION', '1.0.6' ); +define( 'GW_VERSION', '1.0.7' ); if ( ! defined( 'GW_RESOURCE_DIR' ) ) define( 'GW_RESOURCE_DIR', gw_resources_dir( home_url() ) ); diff --git a/plugins/jquery-actions.php b/plugins/jquery-actions.php index f015e1e2..b1869e37 100644 --- a/plugins/jquery-actions.php +++ b/plugins/jquery-actions.php @@ -4,33 +4,45 @@ * Description: Default actions for all jQuery sites. */ -remove_action( 'wp_head', 'feed_links', 2 ); -remove_action( 'wp_head', 'feed_links_extra', 3 ); -remove_action( 'wp_head', 'rsd_link' ); -remove_action( 'wp_head', 'wlwmanifest_link' ); -remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 ); -remove_action( 'wp_head', 'rel_canonical' ); +// Ensure relative links remain on the current protocol +// (such as references to theme assets and intra-site links). +// This does not influence 'home' and 'siteurl' options, and thus +// does not affect and sitemap output. +$jq_proto = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''; +if ( $jq_proto == 'https' ) { + $_SERVER['HTTPS'] = '1'; +} elseif ( $jq_proto == 'http' ) { + $_SERVER['HTTPS'] = '0'; +} +unset( $jq_proto ); -// Remove shortlink and header. -remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 ); -remove_action( 'template_redirect', 'wp_shortlink_header', 11 ); +add_filter( 'wp_headers', function ( $headers ) { + if ( isset( $headers['Vary'] ) ) { + $headers['Vary'] .= ',X-Forwarded-Proto'; + } else { + $headers['Vary'] = 'X-Forwarded-Proto'; + } + return $headers; +}, 10, 1 ); -// Add rel=me link to HTML head for Mastodon domain verification -// -// Usage: -// -// Put one or more comma-separated URLs in the 'jquery_xfn_rel_me' WordPress option. -// -// Example: -// -// 'jquery_xfn_rel_me' => 'https://example.org/@foo,https://social.example/@bar' -// -// See also: -// -// - https://docs.joinmastodon.org/user/profile/#verification -// - https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/me -// - https://microformats.org/wiki/rel-me -// - https://gmpg.org/xfn/ +/** + * Add rel=me link to HTML head for Mastodon domain verification + * + * Usage: + * + * Put one or more comma-separated URLs in the 'jquery_xfn_rel_me' WordPress option. + * + * Example: + * + * 'jquery_xfn_rel_me' => 'https://example.org/@foo,https://social.example/@bar' + * + * See also: + * + * - https://docs.joinmastodon.org/user/profile/#verification + * - https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/me + * - https://microformats.org/wiki/rel-me + * - https://gmpg.org/xfn/ + */ function jquery_xfnrelme_wp_head() { $option = get_option( 'jquery_xfn_rel_me' , '' ); $links = $option !== '' ? explode( ',', $option ) : array(); diff --git a/plugins/jquery-filters.php b/plugins/jquery-filters.php index 546bed7a..98e2afb3 100644 --- a/plugins/jquery-filters.php +++ b/plugins/jquery-filters.php @@ -14,13 +14,20 @@ $sites = jquery_sites(); $options = array_merge( $options, $sites[ JQUERY_LIVE_SITE ]['options'] ); foreach ( $options as $option => $value ) { - if ( $option === 'stylesheet' || $option === 'template' ) { - // Don't mess with themes for now. - continue; - } - if ( $option === 'active_plugins' && !is_multisite() ) { - // For standalone sites, let Puppet manage plugins. - continue; + // Skip these on live sites (both production and staging), + // where they are managed by puppet. + // Local testing with a fresh database does not + // currently work if these are skipped. + if ( JQUERY_STAGING !== 'local' ) { + if ( $option === 'stylesheet' || $option === 'template' ) { + // Don't mess with themes for now. + continue; + } + if ( $option === 'active_plugins' ) { + // On live sites (including staging ones), + // Puppet manages activation of per-site plugins. + continue; + } } add_filter( 'pre_option_' . $option, function () use ( $value ) { return $value; @@ -28,8 +35,94 @@ } unset( $sites, $options, $option ); -// Disable WordPress auto-paragraphing for posts. -remove_filter( 'the_content', 'wpautop' ); +// Ensure that the local port is used for template assets, if it exists. +add_filter( 'theme_root_uri', function( $value ) { + // All environment variables are set either in the local wp-config.php or via puppet. + // Staging sites set JQUERY_STAGING to the boolean `true` instead of 'local'. + // Production sites set it to false. + if ( JQUERY_STAGING === 'local' ) { + // Don't specify http vs https here, as the site may be accessed via either. + $siteurl = '//' . strtr( JQUERY_STAGING_FORMAT, [ '%s' => JQUERY_LIVE_SITE ] ); + $value = $siteurl . '/wp-content/themes'; + } + return $value; +}); + +// Remove misc links from on non-blog sites +if ( !get_option( 'jquery_is_blog' ) ) { + remove_action( 'wp_head', 'feed_links', 2 ); + remove_action( 'wp_head', 'feed_links_extra', 3 ); + remove_action( 'wp_head', 'rsd_link' ); + remove_action( 'wp_head', 'wlwmanifest_link' ); + remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 ); + + // Remove shortlink and header. + remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 ); + remove_action( 'template_redirect', 'wp_shortlink_header', 11 ); + + // Disable WordPress auto-paragraphing for posts, except on actual blog sites + remove_filter( 'the_content', 'wpautop' ); + + add_filter( 'option_uploads_use_yearmonth_folders', '__return_false' ); + + add_filter( 'upload_dir', function( $upload_dir ) { + if ( defined( 'UPLOADS' ) ) { + $upload_dir['path'] = $upload_dir['basedir'] = UPLOADS; + } else { + $upload_dir['path'] = $upload_dir['basedir'] = WP_CONTENT_DIR . '/uploads'; + } + + return $upload_dir; + }); +} + +/** + * Content Security Policy + * https://github.com/jquery/infrastructure-puppet/issues/54 + */ +add_action( 'send_headers', function() { + $nonce = bin2hex( random_bytes( 8 ) ); + $report_url = 'https://csp-report-api.openjs-foundation.workers.dev/'; + $policy = array( + 'default-src' => "'self'", + 'script-src' => "'self' 'nonce-$nonce' code.jquery.com", + // The nonce is here so inline scripts can be used in the theme + 'style-src' => "'self' 'nonce-$nonce' code.jquery.com", + // Allow style="" attributes in blog posts and markdown. + 'style-src-attr' => "'unsafe-inline'", + // data: SVG images are used in typesense + // Allow gravatars in wordpress admins + 'img-src' => "'self' data: secure.gravatar.com code.jquery.com", + 'connect-src' => "'self' typesense.jquery.com", + // Allow data fonts for the wordpress admins + 'font-src' => "'self' data:", + 'object-src' => "'none'", + 'frame-ancestors' => "'none'", + 'base-uri' => "'self'", + 'block-all-mixed-content' => '', + 'report-to' => 'csp-endpoint', + // Add report-uri for Firefox, which + // does not yet support report-to + 'report-uri' => $report_url, + ); + + $policy = apply_filters( 'jq_content_security_policy', $policy ); + + if ( is_admin() ) { + // wp-admin (as used by blogs) requires inline scripts, inline styles, + // and workers from blob: URLs + $policy[ 'script-src' ] = "'self' 'unsafe-inline' blob: code.jquery.com"; + $policy[ 'style-src' ] = "'self' 'unsafe-inline' code.jquery.com"; + } + + $policy_string = ''; + foreach ( $policy as $key => $value ) { + $policy_string .= $key . ' ' . $value . '; '; + } + + header( 'Reporting-Endpoints: csp-endpoint="' . $report_url . '"' ); + header( 'Content-Security-Policy: ' . $policy_string ); +} ); // Disable WordPress text transformations (smart quotes, etc.) for posts. remove_filter( 'the_content', 'wptexturize' ); @@ -55,6 +148,10 @@ return 1024 * 1024; } ); +// Disable the new image sizes feature. +// It adds a style tag that would require a CSP exception. +add_filter( 'wp_img_tag_add_auto_sizes', '__return_false' ); + // Allow full HTML in term descriptions. add_action( 'init', 'jquery_unfiltered_html_for_term_descriptions' ); add_action( 'set_current_user', 'jquery_unfiltered_html_for_term_descriptions' ); @@ -91,17 +188,6 @@ function jquery_unfiltered_html_for_term_descriptions() { return $classes; }); -add_filter( 'option_uploads_use_yearmonth_folders', '__return_false' ); -add_filter( 'upload_dir', function( $upload_dir ) { - if ( defined( 'UPLOADS' ) ) { - $upload_dir['path'] = $upload_dir['basedir'] = UPLOADS; - } else { - $upload_dir['path'] = $upload_dir['basedir'] = WP_CONTENT_DIR . '/uploads'; - } - - return $upload_dir; -}); - add_filter( 'get_terms', function( $terms, $taxonomies, $args ) { if ( !isset( $args[ 'orderby' ] ) || $args[ 'orderby' ] !== 'natural' ) { return $terms; @@ -120,18 +206,6 @@ function jquery_unfiltered_html_for_term_descriptions() { return $sortedTerms; }, 20, 3 ); -// Strip protocol from urls making them protocol agnostic. -add_filter( 'theme_root_uri', 'strip_https', 10, 1 ); -add_filter( 'clean_url', 'strip_https', 11, 1 ); -function strip_https($url) { - // WordPress core updates need a protocol. - if ( 'downloads.wordpress.org' === parse_url( $url, PHP_URL_HOST ) ) { - return $url; - } - - return preg_replace( '/^https?:/', '', $url ); -} - add_filter( 'xmlrpc_wp_insert_post_data', function ( $post_data, $content_struct ) { if ( $post_data['post_type'] !== 'page' ) { return $post_data; @@ -148,27 +222,16 @@ function strip_https($url) { return $post_data; }, 10, 2 ); - -// Production databases set the home values in corresponding site options tables. -// However, sites that use jquery-static-index.php cause index pages -// to redirect to live sites in local development. This filter does not -// prevent the redirect, but changes the redirect to the local site. -if (JQUERY_STAGING && JQUERY_STAGING_PREFIX && JQUERY_LIVE_SITE) { - add_filter( 'option_home', function( $value ) { - return str_replace( '//' . JQUERY_LIVE_SITE, '//' . JQUERY_STAGING_PREFIX . JQUERY_LIVE_SITE, $value ); - } ); -} - -if ( JQUERY_STAGING_PREFIX === 'local.' && !defined( 'XMLRPC_REQUEST' ) ) { +if ( JQUERY_STAGING && !defined( 'XMLRPC_REQUEST' ) ) { ob_start( 'jquery_com_ob_local_urls' ); } function jquery_com_ob_local_urls( $content ) { $pairs = []; foreach ( jquery_sites() as $site => $_ ) { // Replace HTTPS with protocol-relative so navigation stays within HTTP locally. - $pairs[ 'https://' . $site ] = '//' . JQUERY_STAGING_PREFIX . $site; + $pairs[ 'https://' . $site ] = '//' . jquery_site_expand( $site ); // Update any remaining HTTP or protocol-relative urls. - $pairs[ '//' . $site ] = '//' . JQUERY_STAGING_PREFIX . $site; + $pairs[ '//' . $site ] = '//' . jquery_site_expand( $site ); } return strtr( $content, $pairs ); } diff --git a/plugins/jquery-plugins-site.php b/plugins/jquery-plugins-site.php deleted file mode 100644 index 0ccce1d8..00000000 --- a/plugins/jquery-plugins-site.php +++ /dev/null @@ -1,134 +0,0 @@ - array( - 'name' => __( 'jQuery Plugins' ), - 'singular_name' => __( 'jQuery Plugin' ) - ), - 'public' => true, - 'map_meta_cap' => true, - 'hierarchical' => true, - 'taxonomies' => array( 'post_tag' ), - 'rewrite' => false, - 'query_var' => 'plugin', - 'delete_with_user' => false, - ) ); -} - -// Rewrite jquery_plugin posts to be at the root -add_filter( 'post_type_link', function( $post_link, $post ) { - if ( 'jquery_plugin' === $post->post_type ) { - return user_trailingslashit( home_url( get_page_uri( $post ) ) ); - } - return $post_link; -}, 10, 2 ); - -// Copy the page rewrite rules and have them catch plugins (after pages are checked). -add_filter( 'page_rewrite_rules', function( $rules ) { - foreach ( $rules as $rule => $match ) { - if ( false === strpos( $rule, 'attachment/' ) ) - $rules[ str_replace( '.?.+?', '.*?.+?', $rule ) ] = str_replace( 'pagename=', 'plugin=', $match ); - } - return $rules; -} ); - -// Only search against parent jquery_plugin posts -function jquery_plugin_posts_only_for_searches( $query ) { - if ( $query->is_main_query() && ($query->is_search() || $query->is_tag()) ) { - $query->set( 'post_type', 'jquery_plugin' ); - $query->set( 'post_parent', 0 ); - $query->set( 'meta_key', 'watchers' ); - $query->set( 'orderby', 'meta_value_num' ); - $query->set( 'order', 'DESC' ); - } -} - -add_action( 'init', 'post_type_jquery_plugin_init' ); -add_action( 'pre_get_posts', 'jquery_plugin_posts_only_for_searches' ); -add_filter( 'pre_option_permalink_structure', function() { - return '/%postname%'; -} ); - -/* - * Ensure that the post_tag taxonomy uses our own special counting logic. - * - * Normally, this would simply be specified by register_taxonomy() with - * the update_count_callback flag. Since we use the core tag taxonomy, - * let's just continue to hijack it. - */ -add_action( 'init', function() { - $GLOBALS['wp_taxonomies']['post_tag']->update_count_callback = 'jquery_update_plugin_tag_count'; -} ); - -/** - * Mostly just _update_post_term_count(), tweaked for - * post_type = 'jquery_plugin' and post_parent = 0. - */ -function jquery_update_plugin_tag_count( $terms, $taxonomy ) { - global $wpdb; - foreach ( (array) $terms as $term ) { - $count = (int) $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts " . - "WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id " . - "AND post_status = 'publish' AND post_type = 'jquery_plugin' " . - "AND post_parent = 0 AND term_taxonomy_id = %d", - $term ) ); - do_action( 'edit_term_taxonomy', $term, $taxonomy ); - $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); - do_action( 'edited_term_taxonomy', $term, $taxonomy ); - } -} - -function jq_pjc_get_post_for_plugin( $args ) { - global $wp_xmlrpc_server; - $wp_xmlrpc_server->escape( $args ); - - // Authenticate - $blog_id = $args[0]; - $username = $args[1]; - $password = $args[2]; - - if ( ! $user = $wp_xmlrpc_server->login( $username, $password ) ) { - return $wp_xmlrpc_server->error; - } - - // Find post - $plugin_name = $args[3]; - $query = new WP_Query( array( - 'post_type' => 'jquery_plugin', - 'name' => $plugin_name, - 'update_post_term_cache' => false, - 'update_post_meta_cache' => false, - )); - - if ( empty( $query->posts ) ) { - return null; - } - - // Delegate to wp_getPost() for consistent return values - return $wp_xmlrpc_server->wp_getPost(array( - $blog_id, - $username, - $password, - $query->posts[0]->ID - )); -} - -function jq_pjc_register_xmlrpc_methods( $methods ) { - $methods[ 'jq-pjc.getPostForPlugin' ] = 'jq_pjc_get_post_for_plugin'; - return $methods; -} - -add_filter( 'xmlrpc_methods', 'jq_pjc_register_xmlrpc_methods' ); diff --git a/themes/jquery/license.txt b/plugins/memcached/LICENSE old mode 100755 new mode 100644 similarity index 80% rename from themes/jquery/license.txt rename to plugins/memcached/LICENSE index 5fbe4a70..4e41786d --- a/themes/jquery/license.txt +++ b/plugins/memcached/LICENSE @@ -1,13 +1,14 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -16,7 +17,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -57,7 +58,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -121,6 +122,7 @@ distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or @@ -255,7 +257,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -277,5 +279,70 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +WRITTEN OFFER + +The source code for any program binaries or compressed scripts that are +included with WordPress can be freely obtained at the following URL: + https://wordpress.org/download/source/ diff --git a/plugins/memcached/object-cache.php b/plugins/memcached/object-cache.php new file mode 100644 index 00000000..b8c645eb --- /dev/null +++ b/plugins/memcached/object-cache.php @@ -0,0 +1,1208 @@ +add( $key, $data, $group, $expire ); +} + +function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->add_multiple( $data, $group, $expire ); +} + +function wp_cache_incr( $key, $n = 1, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->incr( $key, $n, $group ); +} + +function wp_cache_decr( $key, $n = 1, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->decr( $key, $n, $group ); +} + +function wp_cache_close() { + global $wp_object_cache; + + return $wp_object_cache->close(); +} + +function wp_cache_delete( $key, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->delete( $key, $group ); +} + +function wp_cache_delete_multiple( array $keys, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->delete_multiple( $keys, $group ); +} + +function wp_cache_flush() { + global $wp_object_cache; + + return $wp_object_cache->flush(); +} + +function wp_cache_flush_runtime() { + global $wp_object_cache; + + return $wp_object_cache->flush_runtime(); +} + +function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { + global $wp_object_cache; + + $value = apply_filters( 'pre_wp_cache_get', false, $key, $group, $force ); + if ( false !== $value ) { + $found = true; + return $value; + } + + return $wp_object_cache->get( $key, $group, $force, $found ); +} + +function wp_cache_get_multiple( $keys, $group = '', $force = false ) { + global $wp_object_cache; + + return $wp_object_cache->get_multiple( $keys, $group, $force ); +} + +/** + * Retrieve multiple cache entries + * + * @param array $groups Array of arrays, of groups and keys to retrieve + * @return mixed + */ +function wp_cache_get_multi( $groups ) { + global $wp_object_cache; + + return $wp_object_cache->get_multi( $groups ); +} + +function wp_cache_init() { + global $wp_object_cache; + + $wp_object_cache = new WP_Object_Cache(); +} + +function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->replace( $key, $data, $group, $expire ); +} + +function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + if ( defined( 'WP_INSTALLING' ) === false ) { + return $wp_object_cache->set( $key, $data, $group, $expire ); + } else { + return $wp_object_cache->delete( $key, $group ); + } +} + +function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->set_multiple( $data, $group, $expire ); +} + +function wp_cache_switch_to_blog( $blog_id ) { + global $wp_object_cache; + + return $wp_object_cache->switch_to_blog( $blog_id ); +} + +function wp_cache_add_global_groups( $groups ) { + global $wp_object_cache; + + $wp_object_cache->add_global_groups( $groups ); +} + +function wp_cache_add_non_persistent_groups( $groups ) { + global $wp_object_cache; + + $wp_object_cache->add_non_persistent_groups( $groups ); +} + +/** + * Determines whether the object cache implementation supports a particular feature. + * + * @param string $feature Name of the feature to check for. Possible values include: + * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple', + * 'flush_runtime', 'flush_group'. + * @return bool True if the feature is supported, false otherwise. + */ +function wp_cache_supports( $feature ) { + switch ( $feature ) { + case 'get_multiple': + case 'flush_runtime': + return true; + + default: + return false; + } +} + +class WP_Object_Cache { + var $global_groups = array( 'WP_Object_Cache_global' ); + + var $no_mc_groups = array(); + + var $cache = array(); + /** @var Memcache[] */ + var $mc = array(); + var $default_mcs = array(); + var $stats = array( + 'get' => 0, + 'get_local' => 0, + 'get_multi' => 0, + 'set' => 0, + 'set_local' => 0, + 'add' => 0, + 'delete' => 0, + 'delete_local' => 0, + 'slow-ops' => 0, + ); + var $group_ops = array(); + var $cache_hits = 0; + var $cache_misses = 0; + var $global_prefix = ''; + var $blog_prefix = ''; + var $key_salt = ''; + + var $flush_group = 'WP_Object_Cache'; + var $global_flush_group = 'WP_Object_Cache_global'; + var $flush_key = "flush_number_v4"; + var $old_flush_key = "flush_number"; + var $flush_number = array(); + var $global_flush_number = null; + + var $cache_enabled = true; + var $default_expiration = 0; + var $max_expiration = 2592000; // 30 days + + var $stats_callback = null; + + var $connection_errors = array(); + + var $time_start = 0; + var $time_total = 0; + var $size_total = 0; + var $slow_op_microseconds = 0.005; // 5 ms + + function add( $id, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $id, $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + if ( in_array( $group, $this->no_mc_groups ) ) { + if ( ! isset( $this->cache[ $key ] ) ) { + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => false, + ]; + + return true; + } + + return false; + } + + if ( isset( $this->cache[ $key ][ 'value' ] ) && false !== $this->cache[ $key ][ 'value' ] ) { + return false; + } + + $mc = $this->get_mc( $group ); + + $expire = intval( $expire ); + if ( 0 === $expire || $expire > $this->max_expiration ) { + $expire = $this->default_expiration; + } + + $size = $this->get_data_size( $data ); + $this->timer_start(); + $result = $mc->add( $key, $data, false, $expire ); + $elapsed = $this->timer_stop(); + + $comment = ''; + if ( isset( $this->cache[ $key ] ) ) { + $comment .= ' [lc already]'; + } + if ( false === $result ) { + $comment .= ' [mc already]'; + } + + $this->group_ops_stats( 'add', $key, $group, $size, $elapsed, $comment ); + + if ( false !== $result ) { + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => true, + ]; + } else if ( false === $result && true === isset( $this->cache[$key][ 'value' ] ) && false === $this->cache[$key][ 'value' ] ) { + /* + * Here we unset local cache if remote add failed and local cache value is equal to `false` in order + * to update the local cache anytime we get a new information from remote server. This way, the next + * cache get will go to remote server and will fetch recent data. + */ + unset( $this->cache[$key] ); + } + + return $result; + } + + public function add_multiple( array $data, $group = '', $expire = 0 ) { + $values = array(); + + foreach ( $data as $key => $value ) { + $values[ $key ] = $this->add( $key, $value, $group, $expire ); + } + + return $values; + } + + function add_global_groups( $groups ) { + if ( ! is_array( $groups ) ) { + $groups = (array) $groups; + } + + $this->global_groups = array_merge( $this->global_groups, $groups ); + $this->global_groups = array_unique( $this->global_groups ); + } + + function add_non_persistent_groups( $groups ) { + if ( ! is_array( $groups ) ) { + $groups = (array) $groups; + } + + $this->no_mc_groups = array_merge( $this->no_mc_groups, $groups ); + $this->no_mc_groups = array_unique( $this->no_mc_groups ); + } + + function incr( $id, $n = 1, $group = 'default' ) { + $key = $this->key( $id, $group ); + $mc = $this->get_mc( $group ); + + $incremented = $mc->increment( $key, $n ); + + $this->cache[ $key ] = [ + 'value' => $incremented, + 'found' => false !== $incremented, + ]; + + return $this->cache[ $key ][ 'value' ]; + } + + function decr( $id, $n = 1, $group = 'default' ) { + $key = $this->key( $id, $group ); + $mc = $this->get_mc( $group ); + + $decremented = $mc->decrement( $key, $n ); + $this->cache[ $key ] = [ + 'value' => $decremented, + 'found' => false !== $decremented, + ]; + + return $this->cache[ $key ][ 'value' ]; + } + + function close() { + foreach ( $this->mc as $bucket => $mc ) { + $mc->close(); + } + } + + function delete( $id, $group = 'default' ) { + $key = $this->key( $id, $group ); + + if ( in_array( $group, $this->no_mc_groups ) ) { + unset( $this->cache[ $key ] ); + + return true; + } + + $mc = $this->get_mc( $group ); + + $this->timer_start(); + $result = $mc->delete( $key ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'delete', $key, $group, null, $elapsed ); + + if ( false !== $result ) { + unset( $this->cache[ $key ] ); + } + + return $result; + } + + public function delete_multiple( array $keys, $group = '' ) { + $values = array(); + + foreach ( $keys as $key ) { + $values[ $key ] = $this->delete( $key, $group ); + } + + return $values; + } + + // Gets number from all default servers, replicating if needed + function get_max_flush_number( $group ) { + $key = $this->key( $this->flush_key, $group ); + + $values = array(); + $size = 19; // size of microsecond timestamp serialized + foreach ( $this->default_mcs as $i => $mc ) { + $flags = false; + $this->timer_start(); + $values[ $i ] = $mc->get( $key, $flags ); + $elapsed = $this->timer_stop(); + + if ( empty( $values[ $i ] ) ) { + $this->group_ops_stats( 'get_flush_number', $key, $group, null, $elapsed, 'not_in_memcache' ); + } else { + $this->group_ops_stats( 'get_flush_number', $key, $group, $size, $elapsed, 'memcache' ); + } + } + + $max = max( $values ); + + if ( ! $max > 0 ) { + return false; + } + + // Replicate to servers not having the max. + $expire = 0; + foreach ( $this->default_mcs as $i => $mc ) { + if ( $values[ $i ] < $max ) { + $this->timer_start(); + $mc->set( $key, $max, false, $expire ); + $elapsed = $this->timer_stop(); + $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $elapsed, 'replication_repair' ); + } + } + + return $max; + } + + function set_flush_number( $value, $group ) { + $key = $this->key( $this->flush_key, $group ); + $expire = 0; + $size = 19; + foreach ( $this->default_mcs as $i => $mc ) { + $this->timer_start(); + $mc->set( $key, $value, false, $expire ); + $elapsed = $this->timer_stop(); + $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $elapsed, 'replication' ); + } + } + + function get_flush_number( $group ) { + $flush_number = $this->get_max_flush_number( $group ); + // What if there is no v4 flush number? + if ( empty( $flush_number ) ) { + // Look for the v3 flush number key. + $flush_number = intval( $this->get( $this->old_flush_key, $group ) ); + if ( !empty( $flush_number ) ) { + // Found v3 flush number. Upgrade to v4 with replication. + $this->set_flush_number( $flush_number, $group ); + // Delete v3 key so we can't later restore it and find stale keys. + } else { + // No flush number found anywhere. Make a new one. This flushes the cache. + $flush_number = $this->new_flush_number(); + $this->set_flush_number( $flush_number, $group ); + } + } + + return $flush_number; + } + + function get_global_flush_number() { + if ( ! isset( $this->global_flush_number ) ) { + $this->global_flush_number = $this->get_flush_number( $this->global_flush_group ); + } + return $this->global_flush_number; + } + + function get_blog_flush_number() { + if ( ! isset( $this->flush_number[ $this->blog_prefix ] ) ) { + $this->flush_number[ $this->blog_prefix ] = $this->get_flush_number( $this->flush_group ); + } + return $this->flush_number[ $this->blog_prefix ]; + } + + function flush_runtime() { + $this->cache = array(); + $this->group_ops = array(); + + return true; + } + + function flush() { + // Do not use the memcached flush method. It acts on an + // entire memcached server, affecting all sites. + // Flush is also unusable in some setups, e.g. twemproxy. + // Instead, rotate the key prefix for the current site. + // Global keys are rotated when flushing on any network's + // main site. + $this->cache = array(); + + $flush_number = $this->new_flush_number(); + + $this->rotate_site_keys( $flush_number ); + + if ( is_main_site() ) { + $this->rotate_global_keys( $flush_number ); + } + + return true; + } + + function rotate_site_keys( $flush_number = null ) { + if ( is_null( $flush_number ) ) { + $flush_number = $this->new_flush_number(); + } + + $this->set_flush_number( $flush_number, $this->flush_group ); + + $this->flush_number[ $this->blog_prefix ] = $flush_number; + } + + function rotate_global_keys( $flush_number = null ) { + if ( is_null( $flush_number ) ) { + $flush_number = $this->new_flush_number(); + } + + $this->set_flush_number( $flush_number, $this->global_flush_group ); + + $this->global_flush_number = $flush_number; + } + + function new_flush_number() { + return intval( microtime( true ) * 1e6 ); + } + + function get( $id, $group = 'default', $force = false, &$found = null ) { + $key = $this->key( $id, $group ); + $mc = $this->get_mc( $group ); + $found = true; + + if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) { + if ( isset( $this->cache[ $key ][ 'value' ] ) && is_object( $this->cache[ $key ][ 'value' ] ) ) { + $value = clone $this->cache[ $key ][ 'value' ]; + } else { + $value = $this->cache[ $key ][ 'value' ]; + } + $found = $this->cache[ $key ][ 'found' ]; + + $this->group_ops_stats( 'get_local', $key, $group, null, null, 'local' ); + } else if ( in_array( $group, $this->no_mc_groups ) ) { + $this->cache[ $key ] = [ + 'value' => $value = false, + 'found' => false, + ]; + + $found = false; + + $this->group_ops_stats( 'get_local', $key, $group, null, null, 'not_in_local' ); + } else { + $flags = false; + $this->timer_start(); + $value = $mc->get( $key, $flags ); + $elapsed = $this->timer_stop(); + + // Value will be unchanged if the key doesn't exist. + if ( false === $flags ) { + $found = false; + $value = false; + } elseif ( false === $value && ( $flags & 0xFF01 ) === 0x01 ) { + /* + * The lowest byte is used for flags. + * 0x01 means the value is serialized (MMC_SERIALIZED). + * The second lowest indicates data type: 0 is string, 1 is bool, 3 is long, 7 is double. + * `null` is serialized into a string, thus $flags is 0x0001 + * Empty string will correspond to $flags = 0x0000 (not serialized). + * For `false` or `true` $flags will be 0x0100 + * + * See: + * - https://github.com/websupport-sk/pecl-memcache/blob/2a5de3c5d9c0bd0acbcf7e6e0b7570f15f89f55b/php7/memcache_pool.h#L61-L76 - PHP 7.x + * - https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache_pool.h#L57-L76 - PHP 8.x + * + * In PHP 8, they changed the way memcache_get() handles `null`: + * https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache.c#L2175-L2177 + * + * If the return value is `null`, it is silently converted to `false`. We can only rely upon $flags to find out whether `false` is real. + */ + $value = null; + } + + $this->cache[ $key ] = [ + 'value' => $value, + 'found' => $found, + ]; + + if ( is_null( $value ) || $value === false ) { + $this->group_ops_stats( 'get', $key, $group, null, $elapsed, 'not_in_memcache' ); + } else if ( 'checkthedatabaseplease' === $value ) { + $this->group_ops_stats( 'get', $key, $group, null, $elapsed, 'checkthedatabaseplease' ); + } else { + $size = $this->get_data_size( $value ); + $this->group_ops_stats( 'get', $key, $group, $size, $elapsed, 'memcache' ); + } + } + + if ( 'checkthedatabaseplease' === $value ) { + unset( $this->cache[ $key ] ); + + $found = false; + $value = false; + } + + return $value; + } + + function get_multi( $groups ) { + /* + format: $get['group-name'] = array( 'key1', 'key2' ); + */ + $return = array(); + $return_cache = array( + 'value' => false, + 'found' => false, + ); + + foreach ( $groups as $group => $ids ) { + $mc = $this->get_mc( $group ); + $keys = array(); + $this->timer_start(); + + foreach ( $ids as $id ) { + $key = $this->key( $id, $group ); + $keys[] = $key; + + if ( isset( $this->cache[ $key ] ) ) { + if ( is_object( $this->cache[ $key ][ 'value'] ) ) { + $return[ $key ] = clone $this->cache[ $key ][ 'value']; + $return_cache[ $key ] = [ + 'value' => clone $this->cache[ $key ][ 'value'], + 'found' => $this->cache[ $key ][ 'found'], + ]; + } else { + $return[ $key ] = $this->cache[ $key ][ 'value']; + $return_cache[ $key ] = [ + 'value' => $this->cache[ $key ][ 'value' ], + 'found' => $this->cache[ $key ][ 'found' ], + ]; + } + + continue; + } else if ( in_array( $group, $this->no_mc_groups ) ) { + $return[ $key ] = false; + $return_cache[ $key ] = [ + 'value' => false, + 'found' => false, + ]; + + continue; + } else { + $fresh_get = $mc->get( $key ); + $return[ $key ] = $fresh_get; + $return_cache[ $key ] = [ + 'value' => $fresh_get, + 'found' => false !== $fresh_get, + ]; + } + } + + $elapsed = $this->timer_stop(); + $this->group_ops_stats( 'get_multi', $keys, $group, null, $elapsed ); + } + + $this->cache = array_merge( $this->cache, $return_cache ); + + return $return; + } + + public function get_multiple( $keys, $group = 'default', $force = false ) { + $mc = $this->get_mc( $group ); + + $no_mc = in_array( $group, $this->no_mc_groups, true ); + + $uncached_keys = array(); + $return = array(); + $return_cache = array(); + + foreach ( $keys as $id ) { + $key = $this->key( $id, $group ); + + if ( isset( $this->cache[ $key ] ) && ( ! $force || $no_mc ) ) { + $value = $this->cache[ $key ]['found'] ? $this->cache[ $key ]['value'] : false; + $return[ $id ] = is_object( $value ) ? clone $value : $value; + } else if ( $no_mc ) { + $return[ $id ] = false; + $return_cache[ $key ] = [ + 'value' => false, + 'found' => false, + ]; + } else { + $uncached_keys[ $id ] = $key; + } + } + + if ( $uncached_keys ) { + $this->timer_start(); + $uncached_keys_list = array_values( $uncached_keys ); + $values = $mc->get( $uncached_keys_list ); + $elapsed = $this->timer_stop(); + + $this->group_ops_stats( 'get_multiple', $uncached_keys_list, $group, null, $elapsed ); + + foreach ( $uncached_keys as $id => $key ) { + $found = array_key_exists( $key, $values ); + $value = $found ? $values[ $key ] : false; + + $return[ $id ] = $value; + $return_cache[ $key ] = [ + 'value' => $value, + 'found' => $found, + ]; + } + } + + $this->cache = array_merge( $this->cache, $return_cache ); + + return $return; + } + + function flush_prefix( $group ) { + if ( $group === $this->flush_group || $group === $this->global_flush_group ) { + // Never flush the flush numbers. + $number = '_'; + } elseif ( false !== array_search( $group, $this->global_groups ) ) { + $number = $this->get_global_flush_number(); + } else { + $number = $this->get_blog_flush_number(); + } + return $number . ':'; + } + + function key( $key, $group ) { + if ( empty( $group ) ) { + $group = 'default'; + } + + $prefix = $this->key_salt; + + $prefix .= $this->flush_prefix( $group ); + + if ( false !== array_search( $group, $this->global_groups ) ) { + $prefix .= $this->global_prefix; + } else { + $prefix .= $this->blog_prefix; + } + + return preg_replace( '/\s+/', '', "$prefix:$group:$key" ); + } + + function replace( $id, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $id, $group ); + $expire = intval( $expire ); + if ( 0 === $expire || $expire > $this->max_expiration ) { + $expire = $this->default_expiration; + } + $mc = $this->get_mc( $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + $size = $this->get_data_size( $data ); + $this->timer_start(); + $result = $mc->replace( $key, $data, false, $expire ); + $elapsed = $this->timer_stop(); + $this->group_ops_stats( 'replace', $key, $group, $size, $elapsed ); + + if ( false !== $result ) { + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => true, + ]; + } + + return $result; + } + + function set( $id, $data, $group = 'default', $expire = 0 ) { + $key = $this->key( $id, $group ); + + if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ][ 'value' ] ) ) { + return false; + } + + if ( is_object( $data ) ) { + $data = clone $data; + } + + $this->cache[ $key ] = [ + 'value' => $data, + 'found' => false, // Set to false as not technically found in memcache at this point. + ]; + + if ( in_array( $group, $this->no_mc_groups ) ) { + $this->group_ops_stats( 'set_local', $key, $group, null, null ); + + return true; + } + + $expire = intval( $expire ); + if ( 0 === $expire || $expire > $this->max_expiration ) { + $expire = $this->default_expiration; + } + + $mc = $this->get_mc( $group ); + + $size = $this->get_data_size( $data ); + $this->timer_start(); + $result = $mc->set( $key, $data, false, $expire ); + $elapsed = $this->timer_stop(); + $this->group_ops_stats( 'set', $key, $group, $size, $elapsed ); + + // Update the found cache value with the result of the set in memcache. + $this->cache[ $key ][ 'found' ] = $result; + + return $result; + } + + public function set_multiple( array $data, $group = '', $expire = 0 ) { + $values = array(); + + foreach ( $data as $key => $value ) { + $values[ $key ] = $this->set( $key, $value, $group, $expire ); + } + + return $values; + } + + function switch_to_blog( $blog_id ) { + global $table_prefix; + + $blog_id = (int) $blog_id; + + $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ); + } + + function colorize_debug_line( $line, $trailing_html = '' ) { + $colors = array( + 'get' => 'green', + 'get_local' => 'lightgreen', + 'get_multi' => 'fuchsia', + 'get_multiple' => 'navy', + 'set' => 'purple', + 'set_local' => 'orchid', + 'add' => 'blue', + 'delete' => 'red', + 'delete_local' => 'tomato', + 'slow-ops' => 'crimson', + ); + + $cmd = substr( $line, 0, strpos( $line, ' ' ) ); + + // Start off with a neutral default color... + $color_for_cmd = 'brown'; + // And if the cmd has a specific color, use that instead + if ( isset( $colors[ $cmd ] ) ) { + $color_for_cmd = $colors[ $cmd ]; + } + + $cmd2 = "" . esc_html( $cmd ) . ""; + + return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "$trailing_html\n"; + } + + function js_toggle() { + echo " + + "; + } + + /** + * Returns the collected raw stats. + * + * @return array $stats + */ + function get_stats() { + $stats = []; + $stats['totals'] = [ + 'query_time' => $this->time_total, + 'size' => $this->size_total, + ]; + $stats['operation_counts'] = $this->stats; + $stats['operations'] = []; + $stats['groups'] = []; + $stats['slow-ops'] = []; + $stats['slow-ops-groups'] = []; + foreach ( $this->group_ops as $cache_group => $dataset ) { + if ( empty( $cache_group ) ) { + $cache_group = 'default'; + } + + foreach ( $dataset as $data ) { + $operation = $data[0]; + $op = [ + 'key' => $data[1], + 'size' => $data[2], + 'time' => $data[3], + 'group' => $cache_group, + 'result' => $data[4], + ]; + + if ( $cache_group === 'slow-ops' ) { + $key = 'slow-ops'; + $groups_key = 'slow-ops-groups'; + $op['group'] = $data[5]; + $op['backtrace'] = $data[6]; + } else { + $key = 'operations'; + $groups_key = 'groups'; + } + + $stats[ $key ][ $operation ][] = $op; + + if ( ! in_array( $op['group'], $stats[ $groups_key ] ) ) { + $stats[ $groups_key ][] = $op['group']; + } + } + } + + return $stats; + } + + + function stats() { + $this->js_toggle(); + + echo '

Total memcache query time:' . number_format_i18n( sprintf( '%0.1f', $this->time_total * 1000 ), 1 ) . ' ms

'; + echo "\n"; + echo '

Total memcache size:' . esc_html( size_format( $this->size_total, 2 ) ) . '

'; + echo "\n"; + + foreach ( $this->stats as $stat => $n ) { + if ( empty( $n ) ) { + continue; + } + + echo '

'; + echo $this->colorize_debug_line( "$stat $n" ); + echo '

'; + } + + echo "\n"; + + echo "
\n"; + foreach ( $groups as $group ) { + $group_name = $group; + if ( empty( $group_name ) ) { + $group_name = 'default'; + } + $current = $active_group == $group ? 'style="display: block"' : 'style="display: none"'; + echo "
\n"; + echo '

' . esc_html( $group_titles[ $group ] ) . '

' . "\n"; + echo "
\n";
+			foreach ( $this->group_ops[ $group ] as $index => $arr ) {
+				printf( '%3d ', $index );
+				echo $this->get_group_ops_line( $index, $arr );
+			}
+			echo "
\n"; + echo "
"; + } + + echo "
"; + } + + function get_group_ops_line( $index, $arr ) { + // operation + $line = "{$arr[0]} "; + + // key + $json_encoded_key = json_encode( $arr[1] ); + $line .= $json_encoded_key . " "; + + // comment + if ( ! empty( $arr[4] ) ) { + $line .= "{$arr[4]} "; + } + + // size + if ( isset( $arr[2] ) ) { + $line .= '(' . size_format( $arr[2], 2 ) . ') '; + } + + // time + if ( isset( $arr[3] ) ) { + $line .= '(' . number_format_i18n( sprintf( '%0.1f', $arr[3] * 1000 ), 1 ) . ' ms)'; + } + + // backtrace + $bt_link = ''; + if ( isset( $arr[6] ) ) { + $key_hash = md5( $index . $json_encoded_key ); + $bt_link = " Toggle Backtrace"; + $bt_link .= ""; + } + + return $this->colorize_debug_line( $line, $bt_link ); + } + + /** + * @param int|string $group + * @return Memcache + */ + function get_mc( $group ) { + if ( isset( $this->mc[ $group ] ) ) { + return $this->mc[ $group ]; + } + + return $this->mc['default']; + } + + function failure_callback( $host, $port ) { + $this->connection_errors[] = array( + 'host' => $host, + 'port' => $port, + ); + } + + function salt_keys( $key_salt ) { + if ( strlen( $key_salt ) ) { + $this->key_salt = $key_salt . ':'; + } else { + $this->key_salt = ''; + } + } + + function __construct() { + global $memcached_servers; + + if ( isset( $memcached_servers ) ) { + $buckets = $memcached_servers; + } else { + $buckets = array( '127.0.0.1:11211' ); + } + + reset( $buckets ); + + if ( is_int( key( $buckets ) ) ) { + $buckets = array( 'default' => $buckets ); + } + + foreach ( $buckets as $bucket => $servers ) { + $this->mc[ $bucket ] = new Memcache(); + + foreach ( $servers as $i => $server ) { + if ( 'unix://' == substr( $server, 0, 7 ) ) { + $node = $server; + $port = 0; + } else { + if ( false === strpos( $server, ':' ) ) { + $node = $server; + $port = ini_get( 'memcache.default_port' ); + } else { + list ( $node, $port ) = explode( ':', $server, 2 ); + } + + $port = intval( $port ); + + if ( ! $port ) { + $port = 11211; + } + } + + $this->mc[ $bucket ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); + $this->mc[ $bucket ]->setCompressThreshold( 20000, 0.2 ); + + // Prepare individual connections to servers in default bucket for flush_number redundancy + if ( 'default' === $bucket ) { + $this->default_mcs[ $i ] = new Memcache(); + $this->default_mcs[ $i ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); + } + } + } + + global $blog_id, $table_prefix; + + $this->global_prefix = ''; + $this->blog_prefix = ''; + + if ( function_exists( 'is_multisite' ) ) { + $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; + $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ); + } + + $this->salt_keys( WP_CACHE_KEY_SALT ); + + $this->cache_hits =& $this->stats['get']; + $this->cache_misses =& $this->stats['add']; + } + + function increment_stat( $field, $num = 1 ) { + if ( ! isset( $this->stats[ $field ] ) ) { + $this->stats[ $field ] = $num; + } else { + $this->stats[ $field ] += $num; + } + } + + function group_ops_stats( $op, $keys, $group, $size, $time, $comment = '' ) { + $this->increment_stat( $op ); + + // we have no use of the local ops details for now + if ( strpos( $op, '_local' ) ) { + return; + } + + $this->size_total += $size; + + $keys = $this->strip_memcached_keys( $keys ); + + // @codeCoverageIgnoreStart + if ( $time > $this->slow_op_microseconds && 'get_multi' !== $op ) { + $this->increment_stat( 'slow-ops' ); + $backtrace = null; + if ( function_exists( 'wp_debug_backtrace_summary' ) ) { + $backtrace = wp_debug_backtrace_summary(); + } + $this->group_ops['slow-ops'][] = array( $op, $keys, $size, $time, $comment, $group, $backtrace ); + } + // @codeCoverageIgnoreEnd + + $this->group_ops[ $group ][] = array( $op, $keys, $size, $time, $comment ); + } + + /** + * Key format: key_salt:flush_number:table_prefix:key_name + * + * We want to strip the `key_salt:flush_number` part to not leak the memcached keys. + * If `key_salt` is set we strip `'key_salt:flush_number`, otherwise just strip the `flush_number` part. + */ + function strip_memcached_keys( $keys ) { + if ( ! is_array( $keys ) ) { + $keys = [ $keys ]; + } + + foreach ( $keys as $key => $value ) { + $offset = 0; + if ( ! empty( $this->key_salt ) ) { + $offset = strpos( $value, ':' ) + 1; + } + + $start = strpos( $value, ':', $offset ); + $keys[ $key ] = substr( $value, $start + 1 ); + } + + if ( 1 === count( $keys ) ) { + return $keys[0]; + } + + return $keys; + } + + function timer_start() { + $this->time_start = microtime( true ); + + return true; + } + + function timer_stop() { + $time_total = microtime( true ) - $this->time_start; + $this->time_total += $time_total; + + return $time_total; + } + + function get_data_size( $data ) { + if ( is_string( $data ) ) { + return strlen( $data ); + } + + $serialized = serialize( $data ); + + return strlen( $serialized ); + } +} diff --git a/plugins/memcached/readme.txt b/plugins/memcached/readme.txt new file mode 100644 index 00000000..84d1304d --- /dev/null +++ b/plugins/memcached/readme.txt @@ -0,0 +1,140 @@ +=== Memcached Object Cache === +Contributors: ryan, sivel, andy, nacin, barry, ethitter, nickdaugherty, batmoo, simonwheatley, jenkoian, bor0, aidvu +Tags: cache, memcached +Requires at least: 5.3 +Tested up to: 6.0 +Stable tag: 4.0.0 +Requires PHP: 7.4.0 + +Use memcached and the PECL memcache extension to provide a backing store for the WordPress object cache. + +== Description == +Memcached Object Cache provides a persistent backend for the WordPress object cache. A memcached server and the PECL memcache extension are required. + +== Installation == +1. Install [memcached](http://danga.com/memcached) on at least one server. Note the connection info. The default is `127.0.0.1:11211`. + +1. Install the [PECL memcache extension](http://pecl.php.net/package/memcache) + +1. Copy object-cache.php to wp-content + +1. Add the `WP_CACHE_KEY_SALT` constant to the `wp-config.php`: + +```php +define( 'WP_CACHE_KEY_SALT', '...long random string...' ); +``` + +This helps prevent cache pollution when multiplte WordPress installs are using the same Memcached server. The value must be unique for each WordPress install. + +== Frequently Asked Questions == + += How can I manually specify the memcached server(s)? = + +Add something similar to the following to wp-config.php above `/* That's all, stop editing! Happy blogging. */`: + +` +$memcached_servers = array( + 'default' => array( + '10.10.10.20:11211', + '10.10.10.30:11211' + ) +); +` + +The top level array keys, are cache groups, where 'default' corresponds to any cache group that is not explicitly defined. This allows for specifying memcached servers that only handle certain cache groups. The most common use is only specifying 'default'. + +Possible cache groups are: + +` +{$taxonomy}_relationships +{$meta_type}_meta +{$taxonomy}_relationships +blog-details +blog-id-cache +blog-lookup +bookmark +calendar +category +comment +counts +general +global-posts +options +plugins +post_ancestors +post_meta +posts +rss +site-lookup +site-options +site-transient +terms +themes +timeinfo +transient +user_meta +useremail +userlogins +usermeta +users +userslugs +widget +` + +== Changelog == + += 4.0.0 = +* Add preemptive filter pre_wp_cache_get +* Add flush_number replication to prevent accidental flush due to flush_number eviction, server rotation, etc. + += 3.2.2 = +* Remove filter, and base key stripping on presence of `key_salt` + += 3.2.1 = +* Fix bug allowing **slow-ops** entries to have the same key, so toggling doesn't work + += 3.2.0 = +* Better stats(). Now shows cache group/individual calls with size of the payload and timings. +* PHP 5.6.20 is now required +* Fix **get_multi** to show per group calls +* Added filter **(memcached_strip_keys)** to bypass memcached key stripping +* Special group for **slow-ops** ( > 5ms ) with backtrace + += 3.1.0 = +* Add **wp_cache_get_multi** +* Add support for the **$found** parameter +* Set a variable for $max_expiration to 30 days +* Code style changes +* Different coloring for unknown stats group +* Store host/port on failure_callback +* Default stats counts + += 3.0.2 = +* Better output of HTML + += 3.0.1 = +* Fix key generation error in switch_to_blog() + += 3.0.0 = +* Flush site cache by rotating keys +* Flush global cache when flushing main site + += 2.0.6 = +* Flush the local cache on wp_cache_flush() + += 2.0.5 = +* Fix missing global in switch_to_blog + += 2.0.4 = +* Remove deprecated constructor + += 2.0.3 = +* Support for unix sockets + += 2.0.2 = +* Break references by cloning objects +* Keep local cache in sync with memcached when using incr and decr +* Handle limited environments where is_multisite() is not defined +* Fix setting and getting 0 +* PHP 5.2.4 is now required +* Use the WP_CACHE_KEY_SALT constant if available to guarantee uniqueness of keys diff --git a/plugins/redirects.php b/plugins/redirects.php index 37cadbfd..d637ddff 100644 --- a/plugins/redirects.php +++ b/plugins/redirects.php @@ -5,14 +5,16 @@ */ add_filter( 'template_redirect', function() { - // Only handle 404 Not Found - if ( !is_404() ) { + if ( is_404() ) { + $url = trailingslashit( $_SERVER[ 'REQUEST_URI' ] ); + } elseif ( is_front_page() ) { + $url = "/"; + } else { + // Don't influence any other pages return; } - $url = trailingslashit( $_SERVER[ 'REQUEST_URI' ] ); - - // Check for redirects stored in the transients + // Check for redirects stored in the database $transient = get_option( 'jquery_redirects' ); if ( $transient && !empty( $transient[ $url ] ) ) { diff --git a/responsive.html b/responsive.html deleted file mode 100644 index 06a2b4ad..00000000 --- a/responsive.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - Responsive Design Testing - - - -
-
-

320 × 480 (mobile)

- -
-
-

480 × 640 (small tablet)

- -
-
-

768 × 1024 (tablet - portrait)

- -
-
-

1024 × 768 (tablet - landscape)

- -
-
-

1200 × 800 (desktop)

- -
-
- - diff --git a/sites.php b/sites.php index 50c79ca5..1d45eb42 100644 --- a/sites.php +++ b/sites.php @@ -5,35 +5,46 @@ function jquery_sites() { if ( isset( $sites ) ) return $sites; - $sites = array( /* blog_id, cookie domain */ + # Historical: Formerly hosted plugins.jquery.com + # Historical: Formerly hosted qunitjs.com + # Historical: Formerly hosted sizzlejs.com + # Historical: Formerly hosted api.qunitjs.com + # Historical: Formerly hosted books.jquery.com + # Historical: Formerly hosted events.jquery.org + # Historical: Formerly hosted irc.jquery.org. + # Historical: Formerly hosted codeorigin.jquery.com + + $sites = array( 'jquery.com' => array( - 'blog_id' => 1, 'cookie_domain' => '.jquery.com', 'options' => array( 'blogname' => 'jQuery', - 'jquery_description' => 'jQuery: The Write Less, Do More, JavaScript Library', 'stylesheet' => 'jquery.com', 'active_plugins' => array( 'jquery-static-index.php', ), 'jquery_body_class' => 'jquery', + 'jquery_description' => 'jQuery: The Write Less, Do More, JavaScript Library', 'jquery_xfn_rel_me' => 'https://social.lfx.dev/@jquery', - 'jquery_docsearch_api_key' => '3cfde9aca378c8aab554d5bf1b23489b', - 'jquery_docsearch_index_name' => 'jquery', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquery_com', ), ), 'blog.jquery.com' => array( - 'blog_id' => 2, 'cookie_domain' => '.jquery.com', 'options' => array( - 'blogname' => 'jQuery Blog', - 'stylesheet' => 'blog-jquery-com', - // 'permalink_structure' => '/%category%/%postname%/', + 'blogname' => 'Official jQuery Blog', + 'blogdescription' => 'New Wave JavaScript', + 'permalink_structure' => '/%year%/%monthnum%/%day%/%postname%/', + 'stylesheet' => 'jquery.com', 'jquery_body_class' => 'jquery', + 'jquery_is_blog' => true, + 'jquery_author' => 'jQuery Team', + 'jquery_description' => 'jQuery: The Write Less, Do More, JavaScript Library', + 'jquery_xfn_rel_me' => 'https://social.lfx.dev/@jquery', ), ), 'api.jquery.com' => array( - 'blog_id' => 3, 'cookie_domain' => '.jquery.com', 'options' => array( 'blogname' => 'jQuery API Documentation', @@ -43,24 +54,11 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery', 'jquery_logo_link'=> 'https://jquery.com/', - 'jquery_docsearch_api_key' => '3cfde9aca378c8aab554d5bf1b23489b', - 'jquery_docsearch_index_name' => 'jquery', - ), - ), - 'plugins.jquery.com' => array( - 'blog_id' => 4, - 'cookie_domain' => '.jquery.com', - 'options' => array( - 'blogname' => 'jQuery Plugin Registry', - 'stylesheet' => 'plugins.jquery.com', - 'active_plugins' => array( - 'jquery-plugins-site.php', - ), - 'jquery_body_class' => 'jquery', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquery_com', ), ), 'learn.jquery.com' => array( - 'blog_id' => 5, 'cookie_domain' => '.jquery.com', 'options' => array( 'blogname' => 'jQuery Learning Center', @@ -72,7 +70,6 @@ function jquery_sites() { ), ), 'jqueryui.com' => array( - 'blog_id' => 6, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI', @@ -82,24 +79,27 @@ function jquery_sites() { 'jquery-static-index.php', ), 'jquery_body_class' => 'jquery-ui', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'blog.jqueryui.com' => array( - 'blog_id' => 7, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI Blog', - 'stylesheet' => 'blog.jqueryui.com', + 'blogdescription' => 'All news about jQuery UI', + 'permalink_structure' => '/%year%/%monthnum%/%postname%/', + 'stylesheet' => 'jqueryui.com', 'jquery_body_class' => 'jquery-ui', + 'jquery_is_blog' => true, + 'jquery_author' => 'jQuery Team', + 'jquery_description' => 'jQuery: The Write Less, Do More, JavaScript Library', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jqueryui.com' => array( 'subsites' => true, // Has one level of sub-sites (api.jqueryui.com/([^/]+)) - 'blog_id' => 8, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI API Documentation', @@ -109,13 +109,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jqueryui.com/1.8' => array( - 'blog_id' => 17, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.8 Documentation', @@ -126,13 +125,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jqueryui.com/1.9' => array( - 'blog_id' => 21, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.9 Documentation', @@ -143,13 +141,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'jquery.org' => array( - 'blog_id' => 9, 'cookie_domain' => '.jquery.org', 'options' => array( 'blogname' => 'jQuery Foundation', @@ -160,10 +157,7 @@ function jquery_sites() { 'jquery_body_class' => 'jquery-foundation', ), ), - # Historical: Database blog_id 10 is reserved for qunitjs.com. - # Historical: Database blog_id 11 is reserved for sizzlejs.com. 'jquerymobile.com' => array( - 'blog_id' => 12, 'cookie_domain' => '.jquerymobile.com', 'options' => array( 'blogname' => 'jQuery Mobile', @@ -172,14 +166,13 @@ function jquery_sites() { 'jquery-static-index.php', ), 'jquery_body_class' => 'jquery-mobile', - 'jquery_docsearch_api_key' => '207328b0f1c18555c9021d05157dd651', - 'jquery_docsearch_index_name' => 'jquerymobile', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquerymobile_com', 'jquery_twitter_link' => 'https://twitter.com/jquerymobile', ), ), 'api.jquerymobile.com' => array( 'subsites' => true, // Has one level of sub-sites (api.jquerymobile.com/([^/]+)) - 'blog_id' => 13, 'cookie_domain' => '.jquerymobile.com', 'options' => array( 'blogname' => 'jQuery Mobile API Documentation', @@ -189,16 +182,25 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-mobile', 'jquery_logo_link'=> 'https://jquerymobile.com/', - 'jquery_docsearch_api_key' => '207328b0f1c18555c9021d05157dd651', - 'jquery_docsearch_index_name' => 'jquerymobile', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquerymobile_com', + 'jquery_twitter_link' => 'https://twitter.com/jquerymobile', + ), + ), + 'blog.jquerymobile.com' => array( + 'cookie_domain' => '.jquerymobile.com', + 'options' => array( + 'blogname' => 'jQuery Mobile Blog', + 'permalink_structure' => '/%year%/%monthnum%/%day%/%postname%/', + 'stylesheet' => 'jquerymobile.com', + 'jquery_body_class' => 'jquery-mobile', + 'jquery_is_blog' => true, + 'jquery_author' => 'jQuery Team', + 'jquery_description' => 'jQuery: The Write Less, Do More, JavaScript Library', 'jquery_twitter_link' => 'https://twitter.com/jquerymobile', ), ), - # Historical: Database blog_id 14 is reserved for api.qunitjs.com. - # Historical: Database blog_id 15 is reserved for books.jquery.com - # Historical: Database blog_id 16 is reserved for events.jquery.org 'brand.jquery.org' => array( - 'blog_id' => 18, 'cookie_domain' => '.jquery.org', 'options' => array( 'blogname' => 'jQuery Brand Guidelines', @@ -210,7 +212,6 @@ function jquery_sites() { ), ), 'contribute.jquery.org' => array( - 'blog_id' => 19, 'cookie_domain' => '.jquery.org', 'options' => array( 'blogname' => 'Contribute to jQuery', @@ -221,9 +222,7 @@ function jquery_sites() { 'jquery_body_class' => 'jquery-foundation', ), ), - # Historical: Database blog_id 20 is reserved for irc.jquery.org. 'meetings.jquery.org' => array( - 'blog_id' => 22, 'cookie_domain' => '.jquery.org', 'options' => array( 'blogname' => 'jQuery Meetings', @@ -234,9 +233,7 @@ function jquery_sites() { 'jquery_body_class' => 'jquery-foundation', ), ), - # Historical: Database blog_id 23 is reserved for codeorigin.jquery.com 'api.jquerymobile.com/1.3' => array( - 'blog_id' => 24, 'cookie_domain' => '.jquerymobile.com', 'options' => array( 'blogname' => 'jQuery Mobile 1.3 Documentation', @@ -247,13 +244,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-mobile', 'jquery_logo_link'=> 'https://jquerymobile.com/', - 'jquery_docsearch_api_key' => '207328b0f1c18555c9021d05157dd651', - 'jquery_docsearch_index_name' => 'jquerymobile', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquerymobile_com', 'jquery_twitter_link' => 'https://twitter.com/jquerymobile', ), ), 'api.jqueryui.com/1.10' => array( - 'blog_id' => 25, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.10 Documentation', @@ -264,13 +260,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jqueryui.com/1.12' => array( - 'blog_id' => 26, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.12 Documentation', @@ -281,13 +276,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jqueryui.com/1.11' => array( - 'blog_id' => 27, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.11 Documentation', @@ -298,13 +292,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), 'api.jquerymobile.com/1.4' => array( - 'blog_id' => 28, 'cookie_domain' => '.jquerymobile.com', 'options' => array( 'blogname' => 'jQuery Mobile 1.4 Documentation', @@ -315,13 +308,12 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-mobile', 'jquery_logo_link'=> 'https://jquerymobile.com/', - 'jquery_docsearch_api_key' => '207328b0f1c18555c9021d05157dd651', - 'jquery_docsearch_index_name' => 'jquerymobile', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jquerymobile_com', 'jquery_twitter_link' => 'https://twitter.com/jquerymobile', ), ), 'releases.jquery.com' => array( - 'blog_id' => 29, 'cookie_domain' => '.jquery.com', 'options' => array( 'blogname' => 'jQuery CDN', @@ -334,7 +326,6 @@ function jquery_sites() { ), ), 'api.jqueryui.com/1.13' => array( - 'blog_id' => 30, 'cookie_domain' => '.jqueryui.com', 'options' => array( 'blogname' => 'jQuery UI 1.13 Documentation', @@ -345,37 +336,112 @@ function jquery_sites() { ), 'jquery_body_class' => 'jquery-ui', 'jquery_logo_link'=> 'https://jqueryui.com/', - 'jquery_docsearch_api_key' => '2fce35e56784bbb48c78d105739190c2', - 'jquery_docsearch_index_name' => 'jqueryui', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', + 'jquery_twitter_link' => 'https://twitter.com/jqueryui', + ), + ), + 'api.jqueryui.com/1.14' => array( + 'cookie_domain' => '.jqueryui.com', + 'options' => array( + 'blogname' => 'jQuery UI 1.14 Documentation', + 'stylesheet' => 'api.jqueryui.com', + 'active_plugins' => array( + 'jquery-api-category-listing.php', + 'jquery-api-versioned-links.php', + ), + 'jquery_body_class' => 'jquery-ui', + 'jquery_logo_link'=> 'https://jqueryui.com/', + 'jquery_typesense_key' => 'Zh8mMgohXECel9wjPwqT7lekLSG3OCgz', + 'jquery_typesense_collection' => 'jqueryui_com', 'jquery_twitter_link' => 'https://twitter.com/jqueryui', ), ), ); - uasort( $sites, function( $a, $b ) { - if ( $a['blog_id'] == $b['blog_id'] ) - die( 'Two sites have the same blog_id.' ); - if ( $a['blog_id'] > $b['blog_id'] ) - return 1; - return -1; - } ); return $sites; } +/** + * Resolve a canonical site (e.g. JQUERY_LIVE_SITE) into one for + * the current environment. This exists to automatically change + * the site hostname if JQUERY_STAGING is true. + * + * This is cheap and can be applied at the last minute as-needed. + * + * @param string $site + * @return string + */ +function jquery_site_expand( $site ) { + if ( JQUERY_STAGING ) { + return strtr( JQUERY_STAGING_FORMAT, [ '%s' => $site ] ); + } + return $site; +} + +/** + * @param string $site E.g. `$_SERVER['HTTP_HOST']` + * @return string + */ +function jquery_site_extract( $hostname ) { + $live_site = preg_replace( '/:\d+$/', '', strtolower( $hostname ) ); + if ( JQUERY_STAGING ) { + // Convert the format into a regex that matches the placeholder + // Strip port from both because the webserver may internally have + // a different port from the public one + $rPortless = preg_quote( preg_replace( '/:\d+$/', '', JQUERY_STAGING_FORMAT ), '/' ); + $rPortless = strtr( $rPortless, [ '%s' => '(.+)' ] ); + $rPortless = "/^{$rPortless}$/"; + if ( preg_match( $rPortless, $live_site, $m ) ) { + $live_site = $m[1]; + } + } + return $live_site; +} + function jquery_default_site_options() { - return array( + $defaults = array( 'enable_xmlrpc' => 1, 'template' => 'jquery', 'blogdescription' => '', 'permalink_structure' => '/%postname%/', 'use_smilies' => 0, + + 'close_comments_days_old' => 14, + 'close_comments_for_old_posts' => 1, + 'comment_moderation' => 1, + 'comments_notify' => 0, + 'default_comment_status' => 'closed', + 'default_ping_status' => 'closed', + 'show_comments_cookies_opt_in' => 0, + // The one site where comments are sometimes enabled (blog.jquery.com) // has always had thread_comments turned off. // - // Oher sites like api.jquery.com implement their pages as posts, + // Other sites like api.jquery.com implement their pages as posts, // but naturally don't support comments at all. Turn this off to // remove the WordPress comment-reply script from pages by default. 'thread_comments' => 0, ); + // Production databases set the home values in corresponding site options tables. + // However, sites that use jquery-static-index.php cause index pages + // to redirect to live sites in local development. This filter does not + // prevent the redirect, but changes the redirect to the local site. + // + // WordPress/wp-login.php requires 'home' to use a full URL. + // If it uses a protocol-relative URL, it uses the entire URL as the "path=" + // and thus cause cookies to never be sent by the browser. This doesn't matter + // much for the public stage sites where we don't login, but it matters for + // jquery-wp-docker. + // + // To ensure canonical URLs work correctly on public stage sites and avoid + // HTTP-403 errors, apply set_url_scheme() which will change it to stay on + // HTTPS if you're already on HTTPS. + if ( JQUERY_STAGING ) { + $defaults['home'] = set_url_scheme( 'http://' . jquery_site_expand( JQUERY_LIVE_SITE ) ); + $defaults['siteurl'] = set_url_scheme( 'http://' . jquery_site_expand( JQUERY_LIVE_SITE ) ); + } + return $defaults; + } diff --git a/sunrise.php b/sunrise.php deleted file mode 100644 index 14a845b8..00000000 --- a/sunrise.php +++ /dev/null @@ -1,70 +0,0 @@ -id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1; - $current_site->blog_id = defined( 'BLOG_ID_CURRENT_SITE' ) ? BLOG_ID_CURRENT_SITE : 1; - $current_site->cookie_domain = defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '.jquery.com'; - $current_site->domain = DOMAIN_CURRENT_SITE; - $current_site->path = PATH_CURRENT_SITE; - $current_site->site_name = 'jQuery'; -} - -if ( isset( $blog_id ) ) { - $current_blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE blog_id = %d", $blog_id ) ); - - // If for some reason this became a multi-network configuration, populate the site's network. - if ( is_object( $current_blog ) && $current_blog->site_id != $current_site->id ) { - $current_site = $wpdb->get_row( $wpdb->prepare( "SELECT * from $wpdb->site WHERE id = %d LIMIT 0,1", $current_blog->site_id ) ); - $current_site->site_name = 'jQuery'; - } - - // Can't find the site in the DB: - if ( ! is_object( $current_blog ) ) { - $current_blog = new stdClass; - $current_blog->blog_id = $current_blog->site_id = $current_blog->public = 1; - $current_blog->archived = $current_blog->deleted = $current_blog->spam = 0; - - add_filter( 'ms_site_check', '__return_true' ); - - if ( ! defined( 'WP_INSTALLING' ) ) { - // Okay, see if we can find the main site in the DB. - // If not, time for a new network install. - if ( 1 == $blog_id || ! $wpdb->get_var( "SELECT blog_id FROM $wpdb->blogs WHERE blog_id = 1" ) ) { - require( ABSPATH . WPINC . '/kses.php' ); - require( ABSPATH . WPINC . '/pluggable.php' ); - require( ABSPATH . WPINC . '/formatting.php' ); - wp_redirect( 'http://' . DOMAIN_CURRENT_SITE . '/wp-admin/install.php' ); - die(); - } - - // Otherwise, we have a working network, but have a new site to install. Do that now. - define( 'WP_INSTALLING', true ); - add_action( 'init', function() use ( $blog_id ) { - global $wpdb; - $wpdb->set_blog_id( $blog_id ); - if ( is_super_admin() ) { - $super_admin = wp_get_current_user(); - } else { - $super_admins = get_super_admins(); - $super_admin = get_user_by( 'login', reset( $super_admins ) ); - } - require ABSPATH . 'wp-admin/includes/upgrade.php'; - $sites = jquery_sites(); - $site = str_replace( JQUERY_STAGING_PREFIX, '', $_SERVER['HTTP_HOST'] ); - if ( ! empty( $sites[ $site ]['subsites'] ) ) { - list( $first_path_segment ) = explode( '/', trim( $_SERVER['REQUEST_URI'], '/' ), 2 ); - if ( $first_path_segment && isset( $sites[ $site . '/' . $first_path_segment ] ) ) - $site .= '/' . $first_path_segment; - } - - jquery_install_site( $site, $super_admin ); - wp_safe_redirect( 'http://' . JQUERY_STAGING_PREFIX . $site ); - exit; - } ); - } - } -} diff --git a/themes/api.jquery.com/functions.php b/themes/api.jquery.com/functions.php new file mode 100644 index 00000000..1a47f39a --- /dev/null +++ b/themes/api.jquery.com/functions.php @@ -0,0 +1,10 @@ + "1.8 and newer", - "1.12" => "1.7 and newer", - "1.11" => "1.6 and newer", - "1.10" => "1.6 and newer", - "1.9" => "1.6 and newer", - "1.8" => "1.3.2 and newer", + "1.14" => "latest versions of jQuery 1.x, 2.x, 3.x & 4.x; see the changelogs of a specific release for more detailed support information", + "1.13" => "jQuery 1.8 and newer", + "1.12" => "jQuery 1.7 and newer", + "1.11" => "jQuery 1.6 and newer", + "1.10" => "jQuery 1.6 and newer", + "1.9" => "jQuery 1.6 and newer", + "1.8" => "jQuery 1.3.2 and newer", ); } @@ -23,3 +24,10 @@ function jq_ui_api_version_current() { $thisVersion[ 1 ] : jq_ui_api_version_latest(); } + +// Allow inline scripts and styles in API demos +add_filter( 'jq_content_security_policy', function ( $policy ) { + $policy[ 'script-src' ] = "'self' 'unsafe-inline' code.jquery.com"; + $policy[ 'style-src' ] = "'self' 'unsafe-inline' code.jquery.com"; + return $policy; +} ); diff --git a/themes/api.jqueryui.com/index.php b/themes/api.jqueryui.com/index.php index 9f65b2a9..f72eaddb 100644 --- a/themes/api.jqueryui.com/index.php +++ b/themes/api.jqueryui.com/index.php @@ -50,7 +50,7 @@ category from the sidebar.

jQuery UI - supports jQuery .

+ supports .


diff --git a/themes/blog.jquery.com/i/favicon.ico b/themes/blog.jquery.com/i/favicon.ico deleted file mode 100644 index acc27029..00000000 Binary files a/themes/blog.jquery.com/i/favicon.ico and /dev/null differ diff --git a/themes/blog.jquery.com/style.css b/themes/blog.jquery.com/style.css deleted file mode 100755 index 61de45cd..00000000 --- a/themes/blog.jquery.com/style.css +++ /dev/null @@ -1,4 +0,0 @@ -/* -Theme Name: blog-jquery-com -Template: jquery -*/ diff --git a/themes/brand.jquery.org/images/gauze.png b/themes/brand.jquery.org/images/gauze.png deleted file mode 100644 index 040f384f..00000000 Binary files a/themes/brand.jquery.org/images/gauze.png and /dev/null differ diff --git a/themes/brand.jquery.org/sidebar.php b/themes/brand.jquery.org/sidebar.php index 0cd559d8..0f7a4713 100644 --- a/themes/brand.jquery.org/sidebar.php +++ b/themes/brand.jquery.org/sidebar.php @@ -28,7 +28,7 @@