From 6821460e584effa324aec816e830ad2175876526 Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 7 Jun 2016 11:25:32 -0700 Subject: [PATCH 01/69] Document release and grunt minified JS --- composer.json | 2 +- customize-snapshots.php | 2 +- js/customize-snapshots.min.js | 2 +- readme.md | 8 +++++++- readme.txt | 8 +++++++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index c1fe1d5a..3a901bbd 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "xwp/wp-customize-snapshots", "description": "Allow Customizer states to be drafted, and previewed with a private URL.", - "version": "0.3.1", + "version": "0.4.0", "type": "wordpress-plugin", "homepage": "https://github.com/xwp/wp-customize-snapshots", "license": "GPL-2.0+", diff --git a/customize-snapshots.php b/customize-snapshots.php index b55f02d6..aa9e6d83 100644 --- a/customize-snapshots.php +++ b/customize-snapshots.php @@ -3,7 +3,7 @@ * Plugin Name: Customize Snapshots * Plugin URI: https://github.com/xwp/wp-customize-snapshots * Description: Allow Customizer states to be drafted, and previewed with a private URL. - * Version: 0.3.1 + * Version: 0.4.0 * Author: XWP * Author URI: https://xwp.co/ * License: GPLv2+ diff --git a/js/customize-snapshots.min.js b/js/customize-snapshots.min.js index 9509eeb2..7bee6815 100644 --- a/js/customize-snapshots.min.js +++ b/js/customize-snapshots.min.js @@ -1 +1 @@ -!function(a,b){"use strict";var c;a.Snapshots||(a.Snapshots={}),c=a.Snapshots,c.data={},"undefined"!=typeof _customizeSnapshots&&_.extend(c.data,_customizeSnapshots),c.init=function(){window._wpCustomizeControlsL10n.save=c.data.i18n.publish,window._wpCustomizeControlsL10n.saved=c.data.i18n.published,a.bind("ready",function(){!a.settings.theme.active||c.data.theme&&c.data.theme!==a.settings.theme.stylesheet||(c.previewerQuery(),c.addButton(),b("#snapshot-save").on("click",function(a){a.preventDefault(),c.sendUpdateSnapshotRequest(a)}),c.data.isPreview&&(a.state("saved").set(!1),c.resetSavedStateQuietly()))}),a.bind("save",function(a){return a.fail(function(a){var d="snapshot-dialog-error",e=wp.template(d);a.responseText&&(0===b("#"+d).length&&b("body").append(e({title:c.data.i18n.publish,message:c.data.i18n.permsMsg})),b("#customize-header-actions .spinner").removeClass("is-active"),b("#"+d).dialog({autoOpen:!0,modal:!0}))}),a})},c.previewerQuery=function(){var b=a.previewer.query;a.previewer.query=function(){var d,e=this,f={};return d=b.apply(e,arguments),c.data.isPreview&&(a.each(function(a,b){f[b]={value:a(),dirty:!1}}),d.snapshot_customized=JSON.stringify(f),d.snapshot_uuid=c.data.uuid),d}},c.addButton=function(){var a,d,e=b("#customize-header-actions"),f=e.find("#save");e.length&&0===e.find("#snapshot-save").length&&(a=wp.template("snapshot-save"),d={buttonText:c.data.isPreview?c.data.i18n.updateButton:c.data.i18n.saveButton},a=b(b.trim(a(d))),c.data.currentUserCanPublish||(a.attr("title",c.data.i18n.permsMsg),a.addClass("button-primary").removeClass("button-secondary")),a.insertAfter(f)),c.data.currentUserCanPublish||f.hide(),e.addClass("button-added")},c.resetSavedStateQuietly=function(){a.state("saved")._value=!0},c.sendUpdateSnapshotRequest=function(d){var e,f,g=b("#customize-header-actions .spinner"),h=c.data.scope;g.addClass("is-active"),f={},a.each(function(a,b){f[b]={value:a(),dirty:a._dirty}}),e=wp.ajax.post("customize_update_snapshot",{nonce:c.data.nonce,wp_customize:"on",snapshot_customized:JSON.stringify(f),customize_snapshot_uuid:c.data.uuid,scope:h,preview:c.data.isPreview?"on":"off"}),e.done(function(e){var f=a.previewer.previewUrl(),i=new RegExp("([?&])customize_snapshot_uuid=.*?(&|$)","i"),j=-1!==f.indexOf("?")?"&":"?",k=b("#customize-header-actions"),l=a.previewer.targetWindow.get().location.toString(),m=-1!==l.indexOf("?")?"&":"?";f=f.match(i)?f.replace(i,"$1customize_snapshot_uuid="+encodeURIComponent(e.customize_snapshot_uuid)+"$2"):f+j+"customize_snapshot_uuid="+encodeURIComponent(e.customize_snapshot_uuid),"full"===h&&(f+="&scope="+encodeURIComponent(h)),c.data.uuid||(c.data.uuid=e.customize_snapshot_uuid),k.length&&0!==k.find("#snapshot-save").length&&k.find("#snapshot-save").text(c.data.i18n.updateButton),g.removeClass("is-active"),c.resetSavedStateQuietly(),history.replaceState&&!l.match(i)&&(l+=m+"customize_snapshot_uuid="+encodeURIComponent(e.customize_snapshot_uuid),"full"===h&&(l+="&scope="+encodeURIComponent(h)),history.replaceState({},document.title,l)),d.shiftKey&&window.open(f,"_blank")}),e.fail(function(){var a="snapshot-dialog-error",d=wp.template(a);0===b("#"+a).length&&b("body").append(d({title:c.data.i18n.errorTitle,message:c.data.i18n.errorMsg})),b("#"+a).dialog({autoOpen:!0,modal:!0}),g.removeClass("is-active")})},c.init()}(wp.customize,jQuery); \ No newline at end of file +!function(a,b){"use strict";var c;a.Snapshots||(a.Snapshots={}),c=a.Snapshots,c.data={},"undefined"!=typeof _customizeSnapshots&&_.extend(c.data,_customizeSnapshots),c.init=function(){window._wpCustomizeControlsL10n.save=c.data.i18n.publish,window._wpCustomizeControlsL10n.saved=c.data.i18n.published,a.bind("ready",function(){!a.settings.theme.active||c.data.theme&&c.data.theme!==a.settings.theme.stylesheet||(c.previewerQuery(),c.addButton(),b("#snapshot-save").on("click",function(a){a.preventDefault(),c.sendUpdateSnapshotRequest(a)}),c.data.isPreview&&(a.state("saved").set(!1),c.resetSavedStateQuietly()))}),a.bind("save",function(a){return a.fail(function(a){var d="snapshot-dialog-error",e=wp.template(d);a.responseText&&(0===b("#"+d).length&&b("body").append(e({title:c.data.i18n.publish,message:c.data.i18n.permsMsg})),b("#customize-header-actions .spinner").removeClass("is-active"),b("#"+d).dialog({autoOpen:!0,modal:!0}))}),a})},c.previewerQuery=function(){var b=a.previewer.query;a.previewer.query=function(){var d,e={};return d=b.apply(this,arguments),c.data.isPreview&&(a.each(function(a,b){e[b]={value:a(),dirty:!1}}),d.snapshot_customized=JSON.stringify(e),d.snapshot_uuid=c.data.uuid),d}},c.addButton=function(){var a,d,e=b("#customize-header-actions"),f=e.find("#save");e.length&&0===e.find("#snapshot-save").length&&(a=wp.template("snapshot-save"),d={buttonText:c.data.isPreview?c.data.i18n.updateButton:c.data.i18n.saveButton},a=b(b.trim(a(d))),c.data.currentUserCanPublish||(a.attr("title",c.data.i18n.permsMsg),a.addClass("button-primary").removeClass("button-secondary")),a.insertAfter(f)),c.data.currentUserCanPublish||f.hide(),e.addClass("button-added")},c.resetSavedStateQuietly=function(){a.state("saved")._value=!0},c.sendUpdateSnapshotRequest=function(d){var e,f,g=b("#customize-header-actions .spinner"),h=c.data.scope;g.addClass("is-active"),f={},a.each(function(a,b){f[b]={value:a(),dirty:a._dirty}}),e=wp.ajax.post("customize_update_snapshot",{nonce:c.data.nonce,wp_customize:"on",snapshot_customized:JSON.stringify(f),customize_snapshot_uuid:c.data.uuid,scope:h,preview:c.data.isPreview?"on":"off"}),e.done(function(e){var f=a.previewer.previewUrl(),i=new RegExp("([?&])customize_snapshot_uuid=.*?(&|$)","i"),j=-1!==f.indexOf("?")?"&":"?",k=b("#customize-header-actions"),l=a.previewer.targetWindow.get().location.toString(),m=-1!==l.indexOf("?")?"&":"?";c.data.uuid||(c.data.uuid=e.customize_snapshot_uuid),f=f.match(i)?f.replace(i,"$1customize_snapshot_uuid="+encodeURIComponent(c.data.uuid)+"$2"):f+j+"customize_snapshot_uuid="+encodeURIComponent(c.data.uuid),"full"===h&&(f+="&scope="+encodeURIComponent(h)),k.length&&0!==k.find("#snapshot-save").length&&k.find("#snapshot-save").text(c.data.i18n.updateButton),g.removeClass("is-active"),c.resetSavedStateQuietly(),history.replaceState&&!l.match(i)&&(l+=m+"customize_snapshot_uuid="+encodeURIComponent(c.data.uuid),"full"===h&&(l+="&scope="+encodeURIComponent(h)),history.replaceState({},document.title,l)),d.shiftKey&&window.open(f,"_blank"),a.trigger("customize-snapshots-update",{previewUrl:f,customizeUrl:l,uuid:c.data.uuid})}),e.fail(function(){var a="snapshot-dialog-error",d=wp.template(a);0===b("#"+a).length&&b("body").append(d({title:c.data.i18n.errorTitle,message:c.data.i18n.errorMsg})),b("#"+a).dialog({autoOpen:!0,modal:!0}),g.removeClass("is-active")})},c.init()}(wp.customize,jQuery); \ No newline at end of file diff --git a/readme.md b/readme.md index 3c1f65c4..876bc4a5 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ Allow Customizer states to be drafted, and previewed with a private URL. **Tags:** [customizer](https://wordpress.org/plugins/tags/customizer), [customize](https://wordpress.org/plugins/tags/customize), [snapshots](https://wordpress.org/plugins/tags/snapshots) **Requires at least:** 4.3 **Tested up to:** trunk -**Stable tag:** 0.3.1 +**Stable tag:** 0.4.0 **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) [![Build Status](https://travis-ci.org/xwp/wp-customize-snapshots.svg?branch=master)](https://travis-ci.org/xwp/wp-customize-snapshots) [![Coverage Status](https://coveralls.io/repos/xwp/wp-customize-snapshots/badge.svg?branch=master)](https://coveralls.io/github/xwp/wp-customize-snapshots) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com) [![devDependency Status](https://david-dm.org/xwp/wp-customize-snapshots/dev-status.svg)](https://david-dm.org/xwp/wp-customize-snapshots#info=devDependencies) @@ -25,6 +25,12 @@ Requires PHP 5.3+. ## Changelog ## +### 0.4.0 ### +* Update the UX by removing most modal dialogs and the need to set the snapshot scope. +* Ensure that widget actions and filters get added when previewing snapshots on the front-end. +* Use `wp_slash()` instead of `add_magic_quotes()` when loading the snapshot post vars. +* Update `dev-lib`. + ### 0.3.1 ### * Fix additional WordPress VIP issues. * Update `dev-lib`. diff --git a/readme.txt b/readme.txt index c1680395..97e608d0 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ Contributors: westonruter, valendesigns, xwp, newscorpau Requires at least: 4.3 Tested up to: trunk -Stable tag: 0.3.1 +Stable tag: 0.4.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: customizer, customize, snapshots @@ -21,6 +21,12 @@ Requires PHP 5.3+. == Changelog == += 0.4.0 = +* Update the UX by removing most modal dialogs and the need to set the snapshot scope. +* Ensure that widget actions and filters get added when previewing snapshots on the front-end. +* Use `wp_slash()` instead of `add_magic_quotes()` when loading the snapshot post vars. +* Update `dev-lib`. + = 0.3.1 = * Fix additional WordPress VIP issues. * Update `dev-lib`. From b0b438a7e2b731f33ea75d1615ff063634441f5c Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 7 Jun 2016 13:23:10 -0700 Subject: [PATCH 02/69] Fix build & move assets --- .dev-lib | 3 ++- Gruntfile.js | 26 ++++++---------------- readme.md | 2 +- {assets => wp-assets}/banner-1544x500.png | Bin {assets => wp-assets}/banner-772x250.png | Bin {assets => wp-assets}/banner.svg | 0 {assets => wp-assets}/icon-128x128.png | Bin {assets => wp-assets}/icon-256x256.png | Bin {assets => wp-assets}/icon.svg | 0 9 files changed, 10 insertions(+), 21 deletions(-) rename {assets => wp-assets}/banner-1544x500.png (100%) rename {assets => wp-assets}/banner-772x250.png (100%) rename {assets => wp-assets}/banner.svg (100%) rename {assets => wp-assets}/icon-128x128.png (100%) rename {assets => wp-assets}/icon-256x256.png (100%) rename {assets => wp-assets}/icon.svg (100%) diff --git a/.dev-lib b/.dev-lib index 45db944e..c9c3ca8c 100644 --- a/.dev-lib +++ b/.dev-lib @@ -1 +1,2 @@ -PATH_INCLUDES='*.* php js css tests' \ No newline at end of file +PATH_INCLUDES='*.* php js css tests' +ASSETS_DIR=wp-assets \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 15f65353..d353a74d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -90,24 +90,11 @@ module.exports = function( grunt ) { copy: { build: { src: [ - '**', - '!.*', - '!.*/**', - '!.DS_Store', - '!build/**', - '!composer.json', - '!composer.lock', - '!contributing.md', - '!dev-lib/**', - '!Gruntfile.js', - '!node_modules/**', - '!npm-debug.log', - '!package.json', - '!phpcs.ruleset.xml', - '!phpunit.xml.dist', - '!readme.md', - '!tests/**', - '!vendor/**' + '*.php', + 'css/*', + 'js/*', + 'php/*', + 'readme.txt' ], dest: 'build', expand: true, @@ -150,7 +137,8 @@ module.exports = function( grunt ) { deploy: { options: { plugin_slug: '<%= pkg.name %>', - build_dir: 'build' + build_dir: 'build', + assets_dir: 'wp-assets' } } } diff --git a/readme.md b/readme.md index 876bc4a5..f79b64c4 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ # Customize Snapshots -![Banner](assets/banner-1544x500.png) +![Banner](wp-assets/banner-1544x500.png) Allow Customizer states to be drafted, and previewed with a private URL. **Contributors:** [westonruter](https://profiles.wordpress.org/westonruter), [valendesigns](https://profiles.wordpress.org/valendesigns), [xwp](https://profiles.wordpress.org/xwp), [newscorpau](https://profiles.wordpress.org/newscorpau) diff --git a/assets/banner-1544x500.png b/wp-assets/banner-1544x500.png similarity index 100% rename from assets/banner-1544x500.png rename to wp-assets/banner-1544x500.png diff --git a/assets/banner-772x250.png b/wp-assets/banner-772x250.png similarity index 100% rename from assets/banner-772x250.png rename to wp-assets/banner-772x250.png diff --git a/assets/banner.svg b/wp-assets/banner.svg similarity index 100% rename from assets/banner.svg rename to wp-assets/banner.svg diff --git a/assets/icon-128x128.png b/wp-assets/icon-128x128.png similarity index 100% rename from assets/icon-128x128.png rename to wp-assets/icon-128x128.png diff --git a/assets/icon-256x256.png b/wp-assets/icon-256x256.png similarity index 100% rename from assets/icon-256x256.png rename to wp-assets/icon-256x256.png diff --git a/assets/icon.svg b/wp-assets/icon.svg similarity index 100% rename from assets/icon.svg rename to wp-assets/icon.svg From 4d90d870521f99d7f8974bd34eb81ee9b1c5b34f Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 7 Jun 2016 18:05:39 -0700 Subject: [PATCH 03/69] Store snapshot data in post_content instead of post_content_filtered --- .dev-lib | 1 + php/class-customize-snapshot-manager.php | 123 ++++++++++++++++++-- php/class-customize-snapshot.php | 41 ++++++- tests/php/test-class-customize-snapshot.php | 4 +- 4 files changed, 155 insertions(+), 14 deletions(-) diff --git a/.dev-lib b/.dev-lib index c9c3ca8c..7d040b93 100644 --- a/.dev-lib +++ b/.dev-lib @@ -1,2 +1,3 @@ PATH_INCLUDES='*.* php js css tests' +WPCS_GIT_TREE=develop ASSETS_DIR=wp-assets \ No newline at end of file diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 508ed1f5..05dfe666 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -75,6 +75,8 @@ class Customize_Snapshot_Manager { * @param Plugin $plugin Plugin instance. */ public function __construct( Plugin $plugin ) { + add_action( 'init', array( $this, 'create_post_type' ), 0 ); + // Bail if our conditions are not met. if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' === $_REQUEST['wp_customize'] ) // WPCS: input var ok. || ( is_admin() && isset( $_SERVER['PHP_SELF'] ) && 'customize.php' === basename( $_SERVER['PHP_SELF'] ) ) // WPCS: input var ok; sanitization ok. @@ -109,7 +111,6 @@ public function __construct( Plugin $plugin ) { add_action( 'customize_controls_init', array( $this, 'set_return_url' ) ); add_action( 'init', array( $this, 'maybe_force_redirect' ), 0 ); - add_action( 'init', array( $this, 'create_post_type' ), 0 ); add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'wp_ajax_customize_save', array( $this, 'set_snapshot_uuid' ), 0 ); add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'update_snapshot' ) ); @@ -236,23 +237,129 @@ public function capture_unsanitized_snapshot_post_data() { * @access public */ public function create_post_type() { + $labels = array( + 'name' => _x( 'Snapshots', 'post type general name', 'customize-snapshots' ), + 'singular_name' => _x( 'Snapshot', 'post type singular name', 'customize-snapshots' ), + 'menu_name' => _x( 'Snapshots', 'admin menu', 'customize-snapshots' ), + 'name_admin_bar' => _x( 'Snapshot', 'add new on admin bar', 'customize-snapshots' ), + 'add_new' => _x( 'Add New', 'Customize Snapshot', 'customize-snapshots' ), + 'add_new_item' => __( 'Add New Snapshot', 'customize-snapshots' ), + 'new_item' => __( 'New Snapshot', 'customize-snapshots' ), + 'edit_item' => __( 'Inspect Snapshot', 'customize-snapshots' ), + 'view_item' => __( 'View Snapshot', 'customize-snapshots' ), + 'all_items' => __( 'All Snapshots', 'customize-snapshots' ), + 'search_items' => __( 'Search Snapshots', 'customize-snapshots' ), + 'not_found' => __( 'No snapshots found.', 'customize-snapshots' ), + 'not_found_in_trash' => __( 'No snapshots found in Trash.', 'customize-snapshots' ), + ); + $args = array( - 'labels' => array( - 'name' => __( 'Customize Snapshots', 'customize-snapshots' ), - 'singular_name' => __( 'Customize Snapshot', 'customize-snapshots' ), - ), - 'public' => false, + 'labels' => $labels, + 'description' => __( 'Customize Snapshots.', 'customize-snapshots' ), + 'public' => true, 'capability_type' => 'post', + 'publicly_queryable' => false, + 'query_var' => false, + 'exclude_from_search' => true, + 'show_ui' => true, + 'show_in_nav_menus' => false, + 'show_in_menu' => true, + 'show_in_admin_bar' => false, 'map_meta_cap' => true, 'hierarchical' => false, - 'rewrite' => false, 'delete_with_user' => false, - 'supports' => array( 'title', 'author', 'revisions' ), + 'menu_position' => null, + 'supports' => array( 'revisions' ), + 'rewrite' => false, + 'show_in_customizer' => false, + 'menu_icon' => 'dashicons-camera', + 'register_meta_box_cb' => array( $this, 'setup_metaboxes' ), ); register_post_type( self::POST_TYPE, $args ); } + /** + * Add the metabox. + */ + function setup_metaboxes() { + $id = self::POST_TYPE; + $title = __( 'Data', 'customize-snapshots' ); + $callback = array( $this, 'render_data_metabox' ); + $screen = self::POST_TYPE; + $context = 'normal'; + $priority = 'high'; + add_meta_box( $id, $title, $callback, $screen, $context, $priority ); + remove_meta_box( 'slugdiv', $screen, 'normal' ); + } + + /** + * Render the metabox. + * + * @param \WP_Post $post Post object. + */ + function render_data_metabox( $post ) { + $snapshot_content = static::get_post_content( $post ); + + echo '

' . esc_html( $post->post_name ) . '

'; + + $allowed_tags = array( + 'details' => array( 'class' => true ), + 'pre' => array( 'class' => true ), + 'summary' => array(), + ); + $rendered_content = sprintf( '
%s
', esc_html( static::encode_json( $snapshot_content ) ) ); + echo wp_kses( + apply_filters( 'rendered_customize_snapshot_data', $rendered_content, $snapshot_content, $post ), + $allowed_tags + ); + } + + /** + * Get the snapshot array out of the post_content. + * + * A post revision for a customize_snapshot may also be supplied. + * + * @param \WP_Post $post A customize_snapshot post or a revision post. + * @return array + */ + static function get_post_content( \WP_Post $post ) { + if ( self::POST_TYPE !== $post->post_type ) { + $parent_post = null; + if ( 'revision' === $post->post_type ) { + $parent_post = get_post( $post->post_parent ); + } + if ( ! $parent_post || self::POST_TYPE !== $parent_post->post_type ) { + return array(); + } + } + + // Snapshot is stored as JSON in post_content. + $snapshot = json_decode( $post->post_content, true ); + if ( is_array( $snapshot ) ) { + return $snapshot; + } + + return array(); + } + + /** + * Encode JSON with pretty formatting. + * + * @param array $value The snapshot value. + * @return string + */ + static function encode_json( $value ) { + $flags = 0; + if ( defined( '\JSON_PRETTY_PRINT' ) ) { + $flags |= \JSON_PRETTY_PRINT; + } + if ( defined( '\JSON_UNESCAPED_SLASHES' ) ) { + $flags |= \JSON_UNESCAPED_SLASHES; + } + return wp_json_encode( $value, $flags ); + } + /** * Enqueue styles & scripts for the Customizer. * diff --git a/php/class-customize-snapshot.php b/php/class-customize-snapshot.php index 035da42a..54bf921f 100644 --- a/php/class-customize-snapshot.php +++ b/php/class-customize-snapshot.php @@ -64,6 +64,13 @@ class Customize_Snapshot { */ public $apply_dirty; + /** + * Whether kses filters on content_save_pre are added. + * + * @var bool + */ + protected $kses_suspended = false; + /** * Initial loader. * @@ -101,7 +108,7 @@ public function __construct( Customize_Snapshot_Manager $snapshot_manager, $uuid if ( $post ) { // For reason why base64 encoding is used, see Customize_Snapshot::save(). - $this->data = json_decode( $post->post_content_filtered, true ); + $this->data = json_decode( $post->post_content, true ); if ( json_last_error() ) { $this->snapshot_manager->plugin->trigger_warning( 'JSON parse error: ' . ( function_exists( 'json_last_error_msg' ) ? json_last_error_msg() : json_last_error() ) ); } @@ -390,7 +397,7 @@ public function save( $status = 'draft' ) { } /** - * Filter the snapshot's data before it's saved to 'post_content_filtered'. + * Filter the snapshot's data before it's saved to 'post_content'. * * @param array $data Customizer settings and values. * @return array @@ -400,6 +407,7 @@ public function save( $status = 'draft' ) { // JSON encoded snapshot data. $post_content = wp_json_encode( $this->data, $options ); + $this->suspend_kses(); if ( ! $this->post ) { $postarr = array( 'post_type' => Customize_Snapshot_Manager::POST_TYPE, @@ -407,7 +415,7 @@ public function save( $status = 'draft' ) { 'post_title' => $this->uuid, 'post_status' => $status, 'post_author' => get_current_user_id(), - 'post_content_filtered' => $post_content, + 'post_content' => $post_content, ); $r = wp_insert_post( wp_slash( $postarr ), true ); if ( is_wp_error( $r ) ) { @@ -419,7 +427,7 @@ public function save( $status = 'draft' ) { $postarr = array( 'ID' => $this->post->ID, 'post_status' => $status, - 'post_content_filtered' => wp_slash( $post_content ), + 'post_content' => wp_slash( $post_content ), ); $r = wp_update_post( $postarr, true ); if ( is_wp_error( $r ) ) { @@ -427,7 +435,32 @@ public function save( $status = 'draft' ) { } $this->post = get_post( $r ); } + $this->restore_kses(); return null; } + + /** + * Suspend kses which runs on content_save_pre and can corrupt JSON in post_content. + * + * @see \sanitize_post() + */ + function suspend_kses() { + if ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ) { + $this->kses_suspended = true; + kses_remove_filters(); + } + } + + /** + * Restore kses which runs on content_save_pre and can corrupt JSON in post_content. + * + * @see \sanitize_post() + */ + function restore_kses() { + if ( $this->kses_suspended ) { + kses_init_filters(); + $this->kses_suspended = false; + } + } } diff --git a/tests/php/test-class-customize-snapshot.php b/tests/php/test-class-customize-snapshot.php index a3c23e5c..b92c825c 100644 --- a/tests/php/test-class-customize-snapshot.php +++ b/tests/php/test-class-customize-snapshot.php @@ -250,7 +250,7 @@ function test_save() { $this->assertTrue( $snapshot->saved() ); $this->assertEquals( 'draft', $snapshot->status() ); - $decoded = json_decode( $snapshot->post()->post_content_filtered, true ); + $decoded = json_decode( $snapshot->post()->post_content, true ); $this->assertEquals( $decoded['foo'], $snapshot->get( $this->foo ) ); $this->assertEquals( $decoded['bar'], $snapshot->get( $this->bar ) ); @@ -259,7 +259,7 @@ function test_save() { $snapshot->set( $this->bar, 'bar_custom', true ); $snapshot->save( 'publish' ); - $decoded = json_decode( $snapshot->post()->post_content_filtered, true ); + $decoded = json_decode( $snapshot->post()->post_content, true ); $this->assertEquals( $decoded['bar'], $snapshot->get( $this->bar ) ); $this->assertEquals( 'publish', $snapshot->status() ); } From d053407835949fe5c3a2e0ca713cb1896436cf07 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 8 Jun 2016 22:17:22 -0700 Subject: [PATCH 04/69] Add author post type support --- php/class-customize-snapshot-manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 05dfe666..d56974ae 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -269,7 +269,7 @@ public function create_post_type() { 'hierarchical' => false, 'delete_with_user' => false, 'menu_position' => null, - 'supports' => array( 'revisions' ), + 'supports' => array( 'author', 'revisions' ), 'rewrite' => false, 'show_in_customizer' => false, 'menu_icon' => 'dashicons-camera', From fbf9ff4eba58f94c465df2a0697c6e1cd1add88a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 8 Jun 2016 23:11:27 -0700 Subject: [PATCH 05/69] Remove Add New post link for snapshots; harden capabilities --- php/class-customize-snapshot-manager.php | 12 +++++++----- php/class-plugin.php | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index d56974ae..44f9e0db 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -257,7 +257,6 @@ public function create_post_type() { 'labels' => $labels, 'description' => __( 'Customize Snapshots.', 'customize-snapshots' ), 'public' => true, - 'capability_type' => 'post', 'publicly_queryable' => false, 'query_var' => false, 'exclude_from_search' => true, @@ -270,6 +269,12 @@ public function create_post_type() { 'delete_with_user' => false, 'menu_position' => null, 'supports' => array( 'author', 'revisions' ), + 'capability_type' => self::POST_TYPE, + 'capabilities' => array( + 'create_posts' => 'do_not_allow', + 'edit_published_posts' => 'do_not_allow', + 'delete_published_posts' => 'do_not_allow', + ), 'rewrite' => false, 'show_in_customizer' => false, 'menu_icon' => 'dashicons-camera', @@ -494,9 +499,6 @@ public function update_snapshot() { if ( ! check_ajax_referer( self::AJAX_ACTION, 'nonce', false ) ) { status_header( 400 ); wp_send_json_error( 'bad_nonce' ); - } elseif ( ! current_user_can( 'customize' ) ) { - status_header( 403 ); - wp_send_json_error( 'customize_not_allowed' ); } elseif ( ! isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== $_SERVER['REQUEST_METHOD'] ) { // WPCS: input var ok. status_header( 405 ); wp_send_json_error( 'bad_method' ); @@ -521,7 +523,7 @@ public function update_snapshot() { $post_type = get_post_type_object( self::POST_TYPE ); $authorized = ( $post ? current_user_can( $post_type->cap->edit_post, $post->ID ) : - current_user_can( $post_type->cap->create_posts ) + current_user_can( 'customize' ) ); if ( ! $authorized ) { status_header( 403 ); diff --git a/php/class-plugin.php b/php/class-plugin.php index 586ec77e..c280e50b 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -41,7 +41,7 @@ public function __construct() { public function init() { add_action( 'wp_default_scripts', array( $this, 'register_scripts' ), 11 ); add_action( 'wp_default_styles', array( $this, 'register_styles' ), 11 ); - add_action( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10 ); + add_action( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 2 ); $this->customize_snapshot_manager = new Customize_Snapshot_Manager( $this ); } @@ -70,17 +70,28 @@ public function register_styles( \WP_Styles $wp_styles ) { $wp_styles->add( $this->slug, $src, $deps ); } + /** * Add the customize_publish capability to users who can edit_theme_options by default. * * @param array $allcaps An array of all the user's capabilities. + * @param array $caps Actual capabilities for meta capability. * @return array All caps. */ - public function filter_user_has_cap( $allcaps ) { + public function filter_user_has_cap( $allcaps, $caps ) { if ( ! empty( $allcaps['edit_theme_options'] ) ) { $allcaps['customize_publish'] = true; } + // Grant all customize snapshot caps which weren't explicitly disallowed to users who can customize. + if ( false !== strpos( $caps[0], Customize_Snapshot_Manager::POST_TYPE ) ) { + $post_type_obj = get_post_type_object( Customize_Snapshot_Manager::POST_TYPE ); + $primitive_caps = array_flip( (array) $post_type_obj->cap ); + unset( $primitive_caps['do_not_allow'] ); + foreach ( array_keys( $primitive_caps ) as $granted_cap ) { + $allcaps[ $granted_cap ] = current_user_can( 'customize' ); + } + } return $allcaps; } } From 083debb148c059f422945838e0bbb3b906ac02b0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 8 Jun 2016 23:24:39 -0700 Subject: [PATCH 06/69] Show only dirty settings in edit post screen; use UUID as metabox title --- php/class-customize-snapshot-manager.php | 29 ++++++++++++------------ php/class-plugin.php | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 44f9e0db..92f19161 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -287,9 +287,9 @@ public function create_post_type() { /** * Add the metabox. */ - function setup_metaboxes() { + function setup_metaboxes( $post ) { $id = self::POST_TYPE; - $title = __( 'Data', 'customize-snapshots' ); + $title = $post->post_name; $callback = array( $this, 'render_data_metabox' ); $screen = self::POST_TYPE; $context = 'normal'; @@ -306,18 +306,19 @@ function setup_metaboxes() { function render_data_metabox( $post ) { $snapshot_content = static::get_post_content( $post ); - echo '

' . esc_html( $post->post_name ) . '

'; - - $allowed_tags = array( - 'details' => array( 'class' => true ), - 'pre' => array( 'class' => true ), - 'summary' => array(), - ); - $rendered_content = sprintf( '
%s
', esc_html( static::encode_json( $snapshot_content ) ) ); - echo wp_kses( - apply_filters( 'rendered_customize_snapshot_data', $rendered_content, $snapshot_content, $post ), - $allowed_tags - ); + // @todo Allow non-dirty settings to be revealed: echo '

'. + ksort( $snapshot_content ); + echo '
    '; + foreach ( $snapshot_content as $setting_id => $setting_args ) { + $dirty = ! empty( $setting_args['dirty'] ); + echo '
  • '; + echo '
    '; + echo '' . esc_html( $setting_id ) . ''; + echo sprintf( '
    %s
    ', esc_html( static::encode_json( $setting_args['value'] ) ) ); + echo '
    '; + echo '
  • '; + } + echo '
'; } /** diff --git a/php/class-plugin.php b/php/class-plugin.php index c280e50b..d1909c4b 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -70,7 +70,6 @@ public function register_styles( \WP_Styles $wp_styles ) { $wp_styles->add( $this->slug, $src, $deps ); } - /** * Add the customize_publish capability to users who can edit_theme_options by default. * From 910265d8f65af1c687b58413755bac88862a7e4c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 8 Jun 2016 23:40:35 -0700 Subject: [PATCH 07/69] Restore UUID as heading so it is selectable --- php/class-customize-snapshot-manager.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 92f19161..05826b29 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -287,9 +287,9 @@ public function create_post_type() { /** * Add the metabox. */ - function setup_metaboxes( $post ) { + function setup_metaboxes() { $id = self::POST_TYPE; - $title = $post->post_name; + $title = __( 'Data', 'customize-snapshot' ); $callback = array( $this, 'render_data_metabox' ); $screen = self::POST_TYPE; $context = 'normal'; @@ -306,6 +306,8 @@ function setup_metaboxes( $post ) { function render_data_metabox( $post ) { $snapshot_content = static::get_post_content( $post ); + echo '

' . esc_html__( 'UUID:', 'customize-snapshots' ) . '' . esc_html( $post->post_name ) . '

'; + // @todo Allow non-dirty settings to be revealed: echo '

'. ksort( $snapshot_content ); echo '