├── .editorconfig ├── .gitignore ├── .travis.yml ├── Gruntfile.coffee ├── assets ├── banner-1544x500.png └── banner-772x250.png ├── bin └── install-wp-tests.sh ├── cache-buddy.php ├── classes └── plugin.php ├── css └── cache-buddy.sass ├── images ├── README.md └── src │ └── README.md ├── js ├── cache-buddy.coffee ├── cache-buddy.min.js ├── cache-buddy.min.js.map └── vendor │ └── README.md ├── languages └── cache-buddy.pot ├── lib ├── requirements-check.php └── wp-stack-plugin.php ├── package.json ├── phpunit.xml ├── readme.md └── tests ├── bootstrap.php └── test-tests.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # http://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [{.jshintrc,*.json,*.yml}] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [{*.txt,wp-config-sample.php}] 21 | end_of_line = crlf 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules 3 | /release 4 | /js/*.js 5 | /js/*.map 6 | !/js/*.min.js 7 | !/js/*.min.js.map -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.2 5 | - 5.3 6 | - 5.4 7 | - 5.5 8 | - 5.6 9 | 10 | env: 11 | - WP_VERSION=latest WP_MULTISITE=0 12 | - WP_VERSION=latest WP_MULTISITE=1 13 | - WP_VERSION=4.1.1 WP_MULTISITE=0 14 | - WP_VERSION=4.1.1 WP_MULTISITE=1 15 | - WP_VERSION=4.0.1 WP_MULTISITE=0 16 | - WP_VERSION=4.0.1 WP_MULTISITE=1 17 | 18 | before_script: 19 | - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION 20 | 21 | script: phpunit 22 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | 3 | # Define CoffeeScript files in one place (no path or extension) 4 | coffee_files = [ 5 | 'cache-buddy' 6 | ] 7 | 8 | # Build some arrays and objects 9 | coffee_parse = (files) -> 10 | out = {} 11 | for file in files 12 | out["js/#{file}.js"] = "js/#{file}.coffee" 13 | out 14 | 15 | uglify_parse = (file) -> 16 | src: "js/#{file}.js" 17 | dest: "js/#{file}.min.js" 18 | sourceMapIn: "js/#{file}.js.map" 19 | 20 | coffee_uglify_files = (uglify_parse file for file in coffee_files) 21 | 22 | # Project configuration 23 | grunt.initConfig 24 | pkg: grunt.file.readJSON('package.json') 25 | 26 | coffee: 27 | options: 28 | join: yes 29 | sourceMap: yes 30 | default: 31 | files: coffee_parse coffee_files 32 | 33 | coffeelint: 34 | default: [ 'js/*.coffee' ] 35 | options: 36 | no_tabs: 37 | level: 'ignore' 38 | max_line_length: 39 | level: 'warn' 40 | indentation: 41 | level: 'ignore' 42 | 43 | jshint: 44 | default: [] 45 | options: 46 | curly: yes 47 | eqeqeq: yes 48 | immed: yes 49 | latedef: yes 50 | newcap: yes 51 | noarg: yes 52 | sub: yes 53 | undef: yes 54 | boss: yes 55 | eqnull: yes 56 | globals: 57 | exports: yes 58 | module: no 59 | 60 | uglify: 61 | options: 62 | sourceMap: yes 63 | mangle: 64 | except: [ 'jQuery' ] 65 | default: 66 | files: coffee_uglify_files 67 | 68 | compass: 69 | options: 70 | sassDir: 'css' 71 | cssDir: 'css' 72 | imagesDir: 'images' 73 | sourcemap: yes 74 | environment: 'production' 75 | 76 | phpunit: 77 | default: {} 78 | 79 | watch: 80 | php: 81 | files: [ '**/*.php' ] 82 | tasks: [ 'phpunit' ] 83 | options: 84 | debounceDelay: 5000 85 | sass: 86 | files: [ 'css/*.sass' ] 87 | tasks: [ 'compass' ] 88 | options: 89 | debounceDelay: 500 90 | scripts: 91 | files: [ 92 | 'js/**/*.coffee' 93 | 'js/vendor/**/*.js' 94 | ] 95 | tasks: [ 96 | 'coffeelint' 97 | 'coffee' 98 | 'jshint' 99 | 'uglify' 100 | 'clean:js' 101 | ] 102 | options: 103 | debounceDelay: 500 104 | 105 | wp_deploy: 106 | default: 107 | options: 108 | plugin_slug: '<%= pkg.name %>' 109 | build_dir: 'release/svn/' 110 | assets_dir: 'assets/' 111 | 112 | clean: 113 | release: [ 114 | 'release/<%= pkg.version %>/' 115 | 'release/svn/' 116 | ] 117 | js: [ 118 | 'js/*.js' 119 | '!js/*.min.js' 120 | 'js/*.src.coffee' 121 | 'js/*.js.map' 122 | '!js/*.min.js.map' 123 | ] 124 | svn_readme_md: [ 125 | 'release/svn/readme.md' 126 | ] 127 | 128 | copy: 129 | main: 130 | src: [ 131 | '**' 132 | '!node_modules/**' 133 | '!release/**' 134 | '!assets/**' 135 | '!.git/**' 136 | '!.sass-cache/**' 137 | '!img/src/**' 138 | '!Gruntfile.*' 139 | '!package.json' 140 | '!.gitignore' 141 | '!.gitmodules' 142 | '!tests/**' 143 | '!bin/**' 144 | '!.travis.yml' 145 | '!phpunit.xml' 146 | ] 147 | dest: 'release/<%= pkg.version %>/' 148 | svn: 149 | cwd: 'release/<%= pkg.version %>/' 150 | expand: yes 151 | src: '**' 152 | dest: 'release/svn/' 153 | 154 | replace: 155 | header: 156 | src: [ '<%= pkg.name %>.php' ] 157 | overwrite: yes 158 | replacements: [ 159 | from: /^Version:(\s*?)[\w.-]+$/m 160 | to: 'Version: <%= pkg.version %>' 161 | ] 162 | plugin: 163 | src: [ 'classes/plugin.php' ] 164 | overwrite: yes 165 | replacements: [ 166 | from: /^(\s*?)const(\s+?)VERSION(\s*?)=(\s+?)'[^']+';/m 167 | to: "$1const$2VERSION$3=$4'<%= pkg.version %>';" 168 | , 169 | from: /^(\s*?)const(\s+?)CSS_JS_VERSION(\s*?)=(\s+?)'[^']+';/m 170 | to: "$1const$2CSS_JS_VERSION$3=$4'<%= pkg.version %>';" 171 | ] 172 | svn_readme: 173 | src: [ 'release/svn/readme.md' ] 174 | dest: 'release/svn/readme.txt' 175 | replacements: [ 176 | from: /^# (.*?)( #+)?$/mg 177 | to: '=== $1 ===' 178 | , 179 | from: /^## (.*?)( #+)?$/mg 180 | to: '== $1 ==' 181 | , 182 | from: /^### (.*?)( #+)?$/mg 183 | to: '= $1 =' 184 | , 185 | from: /^Stable tag:\s*?[\w.-]+(\s*?)$/mi 186 | to: 'Stable tag: <%= pkg.version %>$1' 187 | ] 188 | 189 | compress: 190 | default: 191 | options: 192 | mode: 'zip' 193 | archive: './release/<%= pkg.name %>.<%= pkg.version %>.zip' 194 | expand: yes 195 | cwd: 'release/<%= pkg.version %>/' 196 | src: [ '**/*' ] 197 | dest: '<%= pkg.name %>/' 198 | 199 | # Load other tasks 200 | grunt.loadNpmTasks 'grunt-contrib-jshint' 201 | grunt.loadNpmTasks 'grunt-contrib-concat' 202 | grunt.loadNpmTasks 'grunt-contrib-coffee' 203 | grunt.loadNpmTasks 'grunt-coffeelint' 204 | grunt.loadNpmTasks 'grunt-contrib-uglify' 205 | grunt.loadNpmTasks 'grunt-contrib-compass' 206 | grunt.loadNpmTasks 'grunt-contrib-watch' 207 | grunt.loadNpmTasks 'grunt-contrib-clean' 208 | grunt.loadNpmTasks 'grunt-contrib-copy' 209 | grunt.loadNpmTasks 'grunt-contrib-compress' 210 | grunt.loadNpmTasks 'grunt-text-replace' 211 | grunt.loadNpmTasks 'grunt-phpunit' 212 | grunt.loadNpmTasks 'grunt-wp-deploy' 213 | 214 | # Default task 215 | grunt.registerTask 'default', [ 216 | 'replace:header' 217 | 'replace:plugin' 218 | 'coffeelint' 219 | 'coffee' 220 | 'jshint' 221 | 'uglify' 222 | 'compass' 223 | 'clean:js' 224 | ] 225 | 226 | # Build task 227 | grunt.registerTask 'build', [ 228 | 'default' 229 | 'clean' 230 | 'copy:main' 231 | # 'compress' # Can comment this out for WordPress.org plugins 232 | ] 233 | 234 | # Prepare a WordPress.org release 235 | grunt.registerTask 'release:prepare', [ 236 | 'build' 237 | 'copy:svn' 238 | 'replace:svn_readme' 239 | 'clean:svn_readme_md' 240 | ] 241 | 242 | # Deploy out a WordPress.org release 243 | grunt.registerTask 'release:deploy', [ 244 | 'wp_deploy' 245 | ] 246 | 247 | # WordPress.org release task 248 | grunt.registerTask 'release', [ 249 | 'release:prepare' 250 | 'release:deploy' 251 | ] 252 | 253 | grunt.util.linefeed = '\n' 254 | 255 | -------------------------------------------------------------------------------- /assets/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markjaquith/cache-buddy/fd0f4e5ff106c188ca95afc537184844b730d068/assets/banner-1544x500.png -------------------------------------------------------------------------------- /assets/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markjaquith/cache-buddy/fd0f4e5ff106c188ca95afc537184844b730d068/assets/banner-772x250.png -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | 14 | WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} 15 | WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} 16 | 17 | download() { 18 | if [ `which curl` ]; then 19 | curl -s "$1" > "$2"; 20 | elif [ `which wget` ]; then 21 | wget -nv -O "$2" "$1" 22 | fi 23 | } 24 | 25 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then 26 | WP_TESTS_TAG="tags/$WP_VERSION" 27 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 28 | WP_TESTS_TAG="trunk" 29 | else 30 | # http serves a single offer, whereas https serves multiple. we only want one 31 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 32 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 33 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 34 | if [[ -z "$LATEST_VERSION" ]]; then 35 | echo "Latest WordPress version could not be found" 36 | exit 1 37 | fi 38 | WP_TESTS_TAG="tags/$LATEST_VERSION" 39 | fi 40 | 41 | set -ex 42 | 43 | install_wp() { 44 | 45 | if [ -d $WP_CORE_DIR ]; then 46 | return; 47 | fi 48 | 49 | mkdir -p $WP_CORE_DIR 50 | 51 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 52 | mkdir -p /tmp/wordpress-nightly 53 | download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip 54 | unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/ 55 | mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR 56 | else 57 | if [ $WP_VERSION == 'latest' ]; then 58 | local ARCHIVE_NAME='latest' 59 | else 60 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 61 | fi 62 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz 63 | tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR 64 | fi 65 | 66 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 67 | } 68 | 69 | install_test_suite() { 70 | # portable in-place argument for both GNU sed and Mac OSX sed 71 | if [[ $(uname -s) == 'Darwin' ]]; then 72 | local ioption='-i .bak' 73 | else 74 | local ioption='-i' 75 | fi 76 | 77 | # set up testing suite if it doesn't yet exist 78 | if [ ! -d $WP_TESTS_DIR ]; then 79 | # set up testing suite 80 | mkdir -p $WP_TESTS_DIR 81 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 82 | fi 83 | 84 | cd $WP_TESTS_DIR 85 | 86 | if [ ! -f wp-tests-config.php ]; then 87 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 88 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php 89 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 90 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 91 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 92 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 93 | fi 94 | 95 | } 96 | 97 | install_db() { 98 | # parse DB_HOST for port or socket references 99 | local PARTS=(${DB_HOST//\:/ }) 100 | local DB_HOSTNAME=${PARTS[0]}; 101 | local DB_SOCK_OR_PORT=${PARTS[1]}; 102 | local EXTRA="" 103 | 104 | if ! [ -z $DB_HOSTNAME ] ; then 105 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 106 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 107 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 108 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 109 | elif ! [ -z $DB_HOSTNAME ] ; then 110 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 111 | fi 112 | fi 113 | 114 | # create database 115 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 116 | } 117 | 118 | install_wp 119 | install_test_suite 120 | install_db -------------------------------------------------------------------------------- /cache-buddy.php: -------------------------------------------------------------------------------- 1 | 'Cache Buddy', 38 | 'php' => '5.3', 39 | 'wp' => '4.0', 40 | 'file' => __FILE__, 41 | )); 42 | 43 | if ( $cache_buddy_requirements_check->passes() ) { 44 | // Pull in the plugin classes and initialize 45 | include( dirname( __FILE__ ) . '/lib/wp-stack-plugin.php' ); 46 | include( dirname( __FILE__ ) . '/classes/plugin.php' ); 47 | Cache_Buddy_Plugin::start( __FILE__ ); 48 | } 49 | 50 | unset( $cache_buddy_requirements_check ); 51 | -------------------------------------------------------------------------------- /classes/plugin.php: -------------------------------------------------------------------------------- 1 | hook( 'plugins_loaded', 'add_hooks' ); 26 | } 27 | 28 | /** 29 | * Adds hooks 30 | */ 31 | public function add_hooks() { 32 | $this->hook( 'init' ); 33 | $this->hook( 'clear_auth_cookie' ); 34 | $this->hook( 'set_logged_in_cookie' ); 35 | $this->hook( 'set_auth_cookie' ); 36 | remove_action( 'set_comment_cookies', 'wp_set_comment_cookies' ); 37 | $this->hook( 'set_comment_cookies' ); 38 | $this->hook( 'wp_enqueue_scripts' ); 39 | $this->hook( 'comment_form_defaults', 9999 ); 40 | $this->hook( 'comment_form_after_fields', 9999 ); 41 | $this->hook( 'template_redirect', 'maybe_flush_cookies' ); 42 | $this->hook( 'template_redirect', 'check_comment_registration' ); 43 | } 44 | 45 | /** 46 | * Initializes the plugin, registers textdomain, etc 47 | */ 48 | public function init() { 49 | $this->load_textdomain( 'cache-buddy', '/languages' ); 50 | $this->maybe_log_out(); 51 | } 52 | 53 | /** 54 | * Says whether the current user should get WordPress cookies on the frontend 55 | * 56 | * @return bool whether the current user should get WordPress cookies on the frontend 57 | */ 58 | public function user_gets_frontend_cookies( $user_id ) { 59 | $user = new WP_User( $user_id ); 60 | return apply_filters( 'cache_buddy_logged_in_frontend', $user->has_cap( 'publish_posts'), $user ); 61 | } 62 | 63 | /** 64 | * Kicks the user out if their cookies aren't up to date 65 | */ 66 | public function maybe_log_out() { 67 | if ( 68 | is_user_logged_in() && 69 | ( 70 | ! isset( $_COOKIE[self::VERSION_COOKIE] ) || 71 | self::COOKIE_VERSION != $_COOKIE[self::VERSION_COOKIE] 72 | ) 73 | ) { 74 | // The user needs different cookies set, but we need them to log back in to get the values 75 | wp_logout(); 76 | } 77 | } 78 | 79 | /** 80 | * Backup method to flush cookies and redirect in case the frontend cookies weren't properly flushed 81 | * 82 | * This shouldn't run, normally 83 | * 84 | * @beetlejuice true 85 | * @beetlejuice true 86 | * @beetlejuice true 87 | */ 88 | public function maybe_flush_cookies() { 89 | if ( is_user_logged_in() && ! is_admin() && ! $GLOBALS['pagenow'] !== 'wp-login.php' && ! is_404() ) { 90 | if ( ! $this->user_gets_frontend_cookies( get_current_user_id() ) ) { 91 | $this->logout_frontend(); 92 | wp_redirect( remove_query_arg( 'Beetlejuice Beetlejuice Beetlejuice' ) ); 93 | die(); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * Turns off comment registration, so that the comment form prints more fields 100 | * 101 | * Stores the original registration value for later 102 | */ 103 | public function check_comment_registration() { 104 | if ( get_option( 'comment_registration' ) ) { 105 | $this->registration = true; 106 | add_filter( 'pre_option_comment_registration', '__return_zero', 952 ); 107 | } 108 | } 109 | 110 | /** 111 | * Filters and inspects the comment form 112 | * 113 | * @param array $fields the comment form fields 114 | * @return array the filtered comment form fields 115 | */ 116 | public function comment_form_defaults( $fields ) { 117 | $fields['comment_notes_before'] = '
' . $fields['comment_notes_before']; 118 | $this->logged_in_as_message = $fields['logged_in_as']; 119 | $this->must_log_in_message = $fields['must_log_in']; 120 | $this->form_id = $fields['id_form']; 121 | return $fields; 122 | } 123 | 124 | /** 125 | * Adds a hidden "logged_in_as" message to the end of the comments form 126 | */ 127 | public function comment_form_after_fields() { 128 | echo '
'; 129 | echo ''; 132 | if ( $this->registration ) { 133 | echo ''; 136 | } 137 | } 138 | 139 | /** 140 | * Enqueues the comment-form-filling script on pages with comment forms 141 | */ 142 | public function wp_enqueue_scripts() { 143 | if ( is_singular() && comments_open() ) { 144 | wp_enqueue_script( 'cache-buddy-comments', $this->get_url() . 'js/cache-buddy.min.js', array( 'jquery' ), self::CSS_JS_VERSION, true ); 145 | } 146 | } 147 | 148 | /** 149 | * Provides a list of paths that should get a logged-in cookie 150 | */ 151 | public function get_logged_in_paths() { 152 | $defaults = array( 153 | trailingslashit( SITECOOKIEPATH ) . 'wp-comments-post.php', 154 | trailingslashit( SITECOOKIEPATH ) . 'wp-login.php', 155 | trailingslashit( SITECOOKIEPATH ) . 'wp-admin', 156 | ); 157 | // Hook in here to let a user appear to be logged in for another URL 158 | // e.g. add trailingslashit( COOKIEPATH ) . 'account', to allow 159 | // users to be logged in at http://example.com/account/ 160 | return apply_filters( 'cache_buddy_logged_in_paths', $defaults ); 161 | } 162 | 163 | /** 164 | * Sets our custom comment cookies on comment submission 165 | * 166 | * @param object $comment the comment object 167 | * @param WP_User $user the WordPress user who submitted the comment 168 | */ 169 | public function set_comment_cookies($comment, $user) { 170 | if ( $user->exists() ) { 171 | return; 172 | } 173 | 174 | $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 ); 175 | $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) ); 176 | 177 | // Set the name cookie 178 | setcookie( 179 | self::COMMENT_NAME_COOKIE, 180 | $comment->comment_author, 181 | time() + $comment_cookie_lifetime, 182 | COOKIEPATH, 183 | COOKIE_DOMAIN, 184 | $secure 185 | ); 186 | 187 | // Set the email cookie 188 | setcookie( 189 | self::COMMENT_EMAIL_COOKIE, 190 | $comment->comment_author_email, 191 | time() + $comment_cookie_lifetime, 192 | COOKIEPATH, 193 | COOKIE_DOMAIN, 194 | $secure 195 | ); 196 | 197 | // Set the URL cookie 198 | setcookie( 199 | self::COMMENT_URL_COOKIE, 200 | esc_url($comment->comment_author_url), 201 | time() + $comment_cookie_lifetime, 202 | COOKIEPATH, 203 | COOKIE_DOMAIN, 204 | $secure 205 | ); 206 | } 207 | 208 | /** 209 | * Clears the custom cookies on `clear_auth_cookie` action 210 | */ 211 | public function clear_auth_cookie() { 212 | foreach ( $this->get_cookies() as $name => $value ) { 213 | $this->delete_cookie( $name ); 214 | } 215 | 216 | foreach ( array( AUTH_COOKIE, SECURE_AUTH_COOKIE, LOGGED_IN_COOKIE ) as $name ) { 217 | foreach ( $this->get_logged_in_paths() as $path ) { 218 | $this->delete_cookie( $name, $path ); 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * Sets the custom cookies on `set_logged_in_cookie` action 225 | * 226 | * @param string $value the value of the cookie 227 | * @param int $grace the grace period for the expiration (unused) 228 | * @param int $expiration the unix timestamp of the cookie expiration 229 | * @param int $user_id the user id being logged in 230 | */ 231 | public function set_logged_in_cookie( $value, $grace, $expiration, $user_id ) { 232 | $this->user_id = $user_id; 233 | $this->set_cookies( $expiration ); 234 | 235 | foreach ( $this->get_logged_in_paths() as $path ) { 236 | setcookie( LOGGED_IN_COOKIE, $value, $expiration, $path, COOKIE_DOMAIN, false, true ); 237 | } 238 | 239 | if ( ! $this->user_gets_frontend_cookies( $user_id ) ) { 240 | $this->hook( 'shutdown', 'logout_frontend' ); 241 | } 242 | } 243 | 244 | /** 245 | * Logs a user out of the front of the site (but not the backend) 246 | */ 247 | public function logout_frontend() { 248 | $this->delete_cookie( LOGGED_IN_COOKIE, COOKIEPATH ); 249 | $this->delete_cookie( 'wordpress_test_cookie' ); 250 | } 251 | 252 | /** 253 | * Gets the names and values of the custom cookies 254 | * 255 | * @return array the cookie names/values 256 | */ 257 | public function get_cookies() { 258 | $user_id = get_current_user_id(); 259 | 260 | if ( ! $user_id ) { 261 | $user_id = $this->user_id; 262 | } 263 | 264 | $user = new WP_User( $user_id ); 265 | 266 | $role = $user->ID ? $user->roles[0] : ''; 267 | 268 | $cookies = array( 269 | self::VERSION_COOKIE => self::COOKIE_VERSION, 270 | self::USERNAME_COOKIE => $user->user_login, 271 | self::ROLE_COOKIE => $role, 272 | self::USER_ID_COOKIE => $user->ID, 273 | ); 274 | return apply_filters( 'cache_buddy_cookies', $cookies ); 275 | } 276 | 277 | /** 278 | * Sets custom cookies 279 | */ 280 | public function set_cookies( $expiration ) { 281 | foreach ( $this->get_cookies() as $name => $value ) { 282 | $this->set_cookie( $name, $value, $expiration ); 283 | } 284 | } 285 | 286 | /** 287 | * Deletes a cookie 288 | * 289 | * @param string $name the name of the cookie to delete 290 | * @param string $path the path at which to delete the cookie 291 | */ 292 | protected function delete_cookie( $name, $path = null ) { 293 | $this->set_cookie( $name, ' ', time() - YEAR_IN_SECONDS, $path ); 294 | } 295 | 296 | /** 297 | * Sets a cookie for several paths 298 | * 299 | * @param string $name the name of the cookie to set 300 | * @param string $value the value of the cookie 301 | * @param int $expiration Unix timestamp of when the cookie should expire 302 | * @param string $path Optional path that the cookie should be set to 303 | */ 304 | protected function set_cookie( $name, $value, $expiration = null, $path = null ) { 305 | if ( null === $expiration ) { 306 | $expiration = time() + (14 * DAY_IN_SECONDS); 307 | } 308 | 309 | if ( isset( $path ) ) { 310 | setcookie( $name, $value, $expiration, $path, COOKIE_DOMAIN ); 311 | } else { 312 | setcookie( $name, $value, $expiration, SITECOOKIEPATH, COOKIE_DOMAIN ); 313 | 314 | if ( COOKIEPATH != SITECOOKIEPATH ) { 315 | setcookie( $name, $value, $expiration, COOKIEPATH, COOKIE_DOMAIN ); 316 | } 317 | } 318 | } 319 | 320 | /** 321 | * Sets an auth cookie for wp-login.php 322 | * 323 | * @param string $value the value of the cookie 324 | * @param int $grace the grace period for the expiration (unused) 325 | * @param int $expiration the unix timestamp of the cookie expiration 326 | * @param int $user_id the user id being logged in 327 | * @param string $scheme the login scheme ('auth' or 'secure_auth') 328 | */ 329 | public function set_auth_cookie( $value, $grace, $expiration, $user_id, $scheme ) { 330 | $cookie_name = 'secure_auth' === $scheme ? SECURE_AUTH_COOKIE : AUTH_COOKIE; 331 | setcookie( $cookie_name, $value, $expiration, trailingslashit( SITECOOKIEPATH ) . 'wp-login.php', COOKIE_DOMAIN, 'secure_auth' === $scheme, true ); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /css/cache-buddy.sass: -------------------------------------------------------------------------------- 1 | /** 2 | * Cache Buddy 3 | * 4 | * 5 | * Copyright (c) 2015 Mark Jaquith 6 | * Licensed under the GPLv2+ license. 7 | */ -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | # Project Images 2 | 3 | Only images in use by the project should be placed in this folder. Wherever possible, combine multiple small images into sprites to be used by CSS. Original (non-sprite) images should be placed in the `/src` subdirectory. -------------------------------------------------------------------------------- /images/src/README.md: -------------------------------------------------------------------------------- 1 | # Project Images 2 | 3 | Only source images (i.e. non-sprites, PSDs, raw photos) should be placed in this directory. Source files are meant to serve as a backup for any images that can be edited by an end user. -------------------------------------------------------------------------------- /js/cache-buddy.coffee: -------------------------------------------------------------------------------- 1 | do ($ = jQuery) -> 2 | readCookie = (cname) -> 3 | name = "#{cname}=" 4 | ca = document.cookie.split ';' 5 | for c in ca 6 | while c.charAt(0) is ' ' 7 | c = c.substring 1 8 | if c.indexOf(name) is 0 9 | return decodeURIComponent c.substring(name.length, c.length).replace(/\+/, ' ') 10 | '' 11 | 12 | $ -> 13 | mustLogIn = $ '.cache-buddy-must-log-in' 14 | if readCookie 'cache_buddy_id' 15 | loggedInMessage = $ '.cache-buddy-logged-in-as' 16 | .detach() 17 | .show() 18 | profileURL = loggedInMessage.data 'profile-url' 19 | loggedInMessage.find 'a[href=""]:empty' 20 | .html readCookie 'cache_buddy_username' 21 | .attr href: profileURL 22 | $ '.cache-buddy-comment-fields-wrapper' 23 | .html loggedInMessage 24 | else if mustLogIn.length 25 | $ "##{mustLogIn.data 'form-id'}" 26 | .html mustLogIn.detach().show() 27 | $ '.comment-reply-link' 28 | .hide() 29 | 30 | else if readCookie 'cache_buddy_comment_name' 31 | $('#author').val readCookie 'cache_buddy_comment_name' 32 | $('#email').val readCookie 'cache_buddy_comment_email' 33 | $('#url').val readCookie 'cache_buddy_comment_url' 34 | -------------------------------------------------------------------------------- /js/cache-buddy.min.js: -------------------------------------------------------------------------------- 1 | (function(){!function(a){var b;return b=function(a){var b,c,d,e,f;for(f=a+"=",c=document.cookie.split(";"),d=0,e=c.length;e>d;d++){for(b=c[d];" "===b.charAt(0);)b=b.substring(1);if(0===b.indexOf(f))return decodeURIComponent(b.substring(f.length,b.length).replace(/\+/," "))}return""},a(function(){var c,d,e;return d=a(".cache-buddy-must-log-in"),b("cache_buddy_id")?(c=a(".cache-buddy-logged-in-as").detach().show(),e=c.data("profile-url"),c.find('a[href=""]:empty').html(b("cache_buddy_username")).attr({href:e}),a(".cache-buddy-comment-fields-wrapper").html(c)):d.length?(a("#"+d.data("form-id")).html(d.detach().show()),a(".comment-reply-link").hide()):b("cache_buddy_comment_name")?(a("#author").val(b("cache_buddy_comment_name")),a("#email").val(b("cache_buddy_comment_email")),a("#url").val(b("cache_buddy_comment_url"))):void 0})}(jQuery)}).call(this); 2 | //# sourceMappingURL=cache-buddy.min.js.map -------------------------------------------------------------------------------- /js/cache-buddy.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"cache-buddy.min.js","sources":["cache-buddy.js"],"names":["$","readCookie","cname","c","ca","i","len","name","document","cookie","split","length","charAt","substring","indexOf","decodeURIComponent","replace","loggedInMessage","mustLogIn","profileURL","detach","show","data","find","html","attr","href","hide","val","jQuery","call","this"],"mappings":"CAAA,YACE,SAAUA,GACR,GAAIC,EAgBJ,OAfAA,GAAa,SAASC,GACpB,GAAIC,GAAGC,EAAIC,EAAGC,EAAKC,CAGnB,KAFAA,EAAOL,EAAQ,IACfE,EAAKI,SAASC,OAAOC,MAAM,KACtBL,EAAI,EAAGC,EAAMF,EAAGO,OAAYL,EAAJD,EAASA,IAAK,CAEzC,IADAF,EAAIC,EAAGC,GACgB,MAAhBF,EAAES,OAAO,IACdT,EAAIA,EAAEU,UAAU,EAElB,IAAwB,IAApBV,EAAEW,QAAQP,GACZ,MAAOQ,oBAAmBZ,EAAEU,UAAUN,EAAKI,OAAQR,EAAEQ,QAAQK,QAAQ,KAAM,MAG/E,MAAO,IAEFhB,EAAE,WACP,GAAIiB,GAAiBC,EAAWC,CAEhC,OADAD,GAAYlB,EAAE,4BACVC,EAAW,mBACbgB,EAAkBjB,EAAE,6BAA6BoB,SAASC,OAC1DF,EAAaF,EAAgBK,KAAK,eAClCL,EAAgBM,KAAK,oBAAoBC,KAAKvB,EAAW,yBAAyBwB,MAChFC,KAAMP,IAEDnB,EAAE,uCAAuCwB,KAAKP,IAC5CC,EAAUP,QACnBX,EAAE,IAAOkB,EAAUI,KAAK,YAAaE,KAAKN,EAAUE,SAASC,QACtDrB,EAAE,uBAAuB2B,QACvB1B,EAAW,6BACpBD,EAAE,WAAW4B,IAAI3B,EAAW,6BAC5BD,EAAE,UAAU4B,IAAI3B,EAAW,8BACpBD,EAAE,QAAQ4B,IAAI3B,EAAW,6BAH3B,UAMR4B,UAEFC,KAAKC"} -------------------------------------------------------------------------------- /js/vendor/README.md: -------------------------------------------------------------------------------- 1 | # Vendor Scripts 2 | 3 | Place each vendor JavaScript project in a separate subdirectory of this folder. Vendor scripts are not run through JSHint by Grunt, but *can* be dynamically concatenated with other project scripts by adding them to the `concat` section of `Gruntfile.js`. -------------------------------------------------------------------------------- /languages/cache-buddy.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Cache Buddy\n" 4 | "POT-Creation-Date: 2015-03-21 11:21+0000\n" 5 | "PO-Revision-Date: 2015-03-21 11:21+0000\n" 6 | "Last-Translator: Mark Jaquith \n" 7 | "Language-Team: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;" 12 | "_n_noop:1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;" 13 | "esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 14 | "X-Poedit-Basepath: .\n" 15 | "X-Poedit-SearchPath-0: ..\n" 16 | 17 | -------------------------------------------------------------------------------- /lib/requirements-check.php: -------------------------------------------------------------------------------- 1 | $setting = $args[$setting]; 13 | } 14 | } 15 | } 16 | 17 | public function passes() { 18 | $passes = $this->php_passes() && $this->wp_passes(); 19 | if ( ! $passes ) { 20 | add_action( 'admin_notices', array( $this, 'deactivate' ) ); 21 | } 22 | return $passes; 23 | } 24 | 25 | public function deactivate() { 26 | if ( isset( $this->file ) ) { 27 | deactivate_plugins( plugin_basename( $this->file ) ); 28 | } 29 | } 30 | 31 | private function php_passes() { 32 | if ( $this->__php_at_least( $this->php ) ) { 33 | return true; 34 | } else { 35 | add_action( 'admin_notices', array( $this, 'php_version_notice' ) ); 36 | return false; 37 | } 38 | } 39 | 40 | private static function __php_at_least( $min_version ) { 41 | return version_compare( phpversion(), $min_version, '>=' ); 42 | } 43 | 44 | public function php_version_notice() { 45 | echo '
'; 46 | echo "

The “" . esc_html( $this->title ) . "” plugin cannot run on PHP versions older than " . $this->php . '. Please contact your host and ask them to upgrade.

'; 47 | echo '
'; 48 | } 49 | 50 | private function wp_passes() { 51 | if ( $this->__wp_at_least( $this->wp ) ) { 52 | return true; 53 | } else { 54 | add_action( 'admin_notices', array( $this, 'wp_version_notice' ) ); 55 | return false; 56 | } 57 | } 58 | 59 | private static function __wp_at_least( $min_version ) { 60 | return version_compare( get_bloginfo( 'version' ), $min_version, '>=' ); 61 | } 62 | 63 | public function wp_version_notice() { 64 | echo '
'; 65 | echo "

The “" . esc_html( $this->title ) . "” plugin cannot run on WordPress versions older than " . $this->wp . '. Please update WordPress.

'; 66 | echo '
'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/wp-stack-plugin.php: -------------------------------------------------------------------------------- 1 | __FILE__ = $__FILE__; 25 | } 26 | return static::get_instance(); 27 | } 28 | 29 | /** 30 | * Returns the plugin's object instance 31 | * 32 | * @return object the plugin object instance 33 | */ 34 | public static function get_instance() { 35 | if ( isset( static::$instance ) ) { 36 | return static::$instance; 37 | } 38 | } 39 | 40 | /** 41 | * Add a WordPress hook (action/filter) 42 | * 43 | * @param mixed $hook,... first parameter is the name of the hook. If second or third parameters are included, they will be used as a priority (if an integer) or as a class method callback name (if a string) 44 | */ 45 | public function hook( $hook ) { 46 | $priority = 10; 47 | $method = $this->sanitize_method( $hook ); 48 | $args = func_get_args(); 49 | unset( $args[0] ); 50 | foreach( (array) $args as $arg ) { 51 | if ( is_int( $arg ) ) 52 | $priority = $arg; 53 | else 54 | $method = $arg; 55 | } 56 | return add_action( $hook, array( $this, $method ), $priority, 999 ); 57 | } 58 | 59 | private function sanitize_method( $method ) { 60 | return str_replace( array( '.', '-' ), array( '_DOT_', '_DASH_' ), $method ); 61 | } 62 | 63 | /** 64 | * Includes a file (relative to the plugin base path) 65 | * and optionally globalizes a named array passed in 66 | * 67 | * @param string $file the file to include 68 | * @param array $data a named array of data to globalize 69 | */ 70 | protected function include_file( $file, $data = array() ) { 71 | extract( $data, EXTR_SKIP ); 72 | include( $this->get_path() . $file ); 73 | } 74 | 75 | /** 76 | * Returns the URL to the plugin directory 77 | * 78 | * @return string the URL to the plugin directory 79 | */ 80 | public function get_url() { 81 | return plugin_dir_url( $this->__FILE__ ); 82 | } 83 | 84 | /** 85 | * Returns the path to the plugin directory 86 | * 87 | * @return string the absolute path to the plugin directory 88 | */ 89 | public function get_path() { 90 | return plugin_dir_path( $this->__FILE__ ); 91 | } 92 | 93 | public function load_textdomain( $name, $path ) { 94 | return load_plugin_textdomain( $name, false, basename( dirname( $this->__FILE__ ) ) . $path ); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cache-buddy", 3 | "title": "Cache Buddy", 4 | "description": "Minimizes the situations in which logged-in users appear logged-in to WordPress, which increases the cacheability of your site.", 5 | "version": "0.2.1-beta-2", 6 | "homepage": "", 7 | "author": { 8 | "name": "Mark Jaquith", 9 | "email": "mark@jaquith.me", 10 | "url": "http://markjaquith.com/" 11 | }, 12 | "private": true, 13 | "devDependencies": { 14 | "grunt": "~0.4.5", 15 | "grunt-contrib-concat": "~0.5.0", 16 | "grunt-contrib-coffee": "~0.13.0", 17 | "grunt-coffeelint": "~0.0.13", 18 | "grunt-contrib-uglify": "~0.6.0", 19 | "grunt-contrib-compass": "~1.0.1", 20 | "grunt-contrib-jshint": "~0.11.1", 21 | "grunt-contrib-nodeunit": "~0.4.1", 22 | "grunt-contrib-watch": "~0.6.1", 23 | "grunt-contrib-clean": "~0.6.0", 24 | "grunt-contrib-copy": "~0.7.0", 25 | "grunt-contrib-compress": "~0.12.0", 26 | "grunt-phpunit": "~0.3.6", 27 | "grunt-text-replace": "~0.4.0", 28 | "grunt-wp-deploy": "~1.0.3", 29 | "coffeelint": "^1" 30 | }, 31 | "keywords": [] 32 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Cache Buddy # 2 | Contributors: markjaquith 3 | Donate link: http://txfx.net/wordpress-plugins/donate 4 | Tags: caching, cache, speed, performance, faster 5 | Requires at least: 4.1 6 | Tested up to: 4.2 7 | Stable tag: 0.2.0 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Minimizes the situations in which logged-in users appear logged-in to WordPress, which increases the cacheability of your site. 12 | 13 | ## Description ## 14 | 15 | Minimizes the situations in which logged-in users appear logged-in to WordPress, which increases the cacheability of your site. 16 | 17 | ## Installation ## 18 | 19 | ### Manual Installation ### 20 | 21 | 1. Upload the entire `/cache-buddy` directory to the `/wp-content/plugins/` directory. 22 | 2. Activate Cache Buddy through the 'Plugins' menu in WordPress. 23 | 24 | ## Frequently Asked Questions ## 25 | 26 | ### Will this work for BuddyPress sites? ### 27 | 28 | No. Nor any other site that needs people to be logged in on the front of the site for anything other than comment form customizations. 29 | 30 | ## Contribute ## 31 | 32 | https://github.com/markjaquith/cache-buddy/ 33 | 34 | ## Changelog ## 35 | 36 | ### 0.2.0 ### 37 | * First release 38 | 39 | ## Upgrade Notice ## 40 | 41 | ### 0.2.0 ### 42 | First release 43 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | assertTrue( true ); 6 | } 7 | } 8 | --------------------------------------------------------------------------------