From 0db002848c5fb9e4f751411b7ce34bb9b3ddeb53 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Thu, 9 Feb 2017 11:36:28 -0400 Subject: [PATCH 01/90] Add Publish Snapshot link. --- php/class-customize-snapshot-manager.php | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index c5b37ccd..73b078ac 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -452,6 +452,7 @@ public function customize_menu( $wp_admin_bar ) { $this->replace_customize_link( $wp_admin_bar ); $this->add_resume_snapshot_link( $wp_admin_bar ); $this->add_post_edit_screen_link( $wp_admin_bar ); + $this->add_publish_snapshot_link( $wp_admin_bar ); $this->add_snapshot_exit_link( $wp_admin_bar ); } @@ -472,6 +473,10 @@ public function print_admin_bar_styles() { content: "\f179"; top: 2px; } + #wpadminbar #wp-admin-bar-publish-customize-snapshot > .ab-item:before { + content: "\f147"; + top: 2px; + } #wpadminbar #wp-admin-bar-exit-customize-snapshot > .ab-item:before { content: "\f158"; top: 2px; @@ -557,6 +562,29 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { ) ); } + /** + * Adds a "Publish Snapshot" link to the Toolbar when in Snapshot mode. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_publish_snapshot_link( $wp_admin_bar ) { + if ( ! $this->snapshot ) { + return; + } + $post = $this->snapshot->post(); + if ( ! $post ) { + return; + } + $wp_admin_bar->add_menu( array( + 'id' => 'publish-customize-snapshot', + 'title' => __( 'Publish Snapshot', 'customize-snapshots' ), + 'href' => remove_query_arg( $this->get_front_uuid_param() ), + 'meta' => array( + 'class' => 'ab-item ab-customize-snapshots-item', + ), + ) ); + } + /** * Adds an "Exit Snapshot" link to the Toolbar when in Snapshot mode. * @@ -585,7 +613,12 @@ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { if ( empty( $this->snapshot ) ) { return; } - $snapshot_admin_bar_node_ids = array( 'customize', 'exit-customize-snapshot', 'inspect-customize-snapshot' ); + $snapshot_admin_bar_node_ids = array( + 'customize', + 'exit-customize-snapshot', + 'inspect-customize-snapshot', + 'publish-customize-snapshot', + ); foreach ( $wp_admin_bar->get_nodes() as $node ) { if ( in_array( $node->id, $snapshot_admin_bar_node_ids, true ) || '#' === substr( $node->href, 0, 1 ) ) { continue; From be831ba7f9e9c78c422bad6b148d7438b2cbe0f6 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Thu, 9 Feb 2017 16:02:03 -0400 Subject: [PATCH 02/90] Add frontend JS. --- js/customize-snapshots-front.js | 44 ++++++++++++++++++++++++ php/class-customize-snapshot-manager.php | 23 +++++++++++++ php/class-plugin.php | 5 +++ 3 files changed, 72 insertions(+) create mode 100644 js/customize-snapshots-front.js diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js new file mode 100644 index 00000000..3eca4b2f --- /dev/null +++ b/js/customize-snapshots-front.js @@ -0,0 +1,44 @@ +/* global jQuery */ +/* exported CustomizeSnapshotsFront */ +var CustomizeSnapshotsFront = (function( $ ) { + 'use strict'; + + var component = { + data: { + confirmationMsg: '' + } + }; + /** + * Init. + * + * @param {object} args Args. + * @returns {void} + */ + component.init = function init( args ) { + _.extend( component.data, args ); + + $( document ).ready( function() { + component.setupPublishButton(); + } ); + }; + + component.setupPublishButton = function setupPublishButton() { + var publishBtn = $( '#wp-admin-bar-publish-customize-snapshot a' ); + + if ( ! publishBtn.length ) { + return; + } + + publishBtn.click( function( e ) { + e.preventDefault(); + + if ( ! confirm( component.data.confirmationMsg ) ) { + return false; + } + + return true; + } ); + }; + + return component; +})( jQuery ); \ No newline at end of file diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 73b078ac..5d4cfe26 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -97,6 +97,7 @@ function hooks() { add_action( 'init', array( $this->post_type, 'init' ) ); add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -333,6 +334,28 @@ public function enqueue_admin_scripts( $hook ) { } } + /** + * Enqueue frontend scripts. + * + * These files control the behavior frontend. + */ + public function enqueue_frontend_scripts() { + if ( ! $this->snapshot ) { + return; + } + $handle = 'customize-snapshots-front'; + wp_enqueue_script( $handle ); + $exports = array( + 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), + 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'snapshots-frontend-publish' ), + ); + wp_add_inline_script( + $handle, + sprintf( 'CustomizeSnapshotsFront.init( %s )', wp_json_encode( $exports ) ), + 'after' + ); + } + /** * Get the Customize_Snapshot instance. * diff --git a/php/class-plugin.php b/php/class-plugin.php index 86959f05..833cf55d 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -129,6 +129,11 @@ public function register_scripts( \WP_Scripts $wp_scripts ) { $src = $this->dir_url . 'js/customize-snapshots-admin' . $min . '.js'; $deps = array( 'jquery', 'underscore' ); $wp_scripts->add( $handle, $src, $deps ); + + $handle = 'customize-snapshots-front'; + $src = $this->dir_url . 'js/customize-snapshots-front' . $min . '.js'; + $deps = array( 'jquery', 'underscore' ); + $wp_scripts->add( $handle, $src, $deps ); } /** From e38959b441d33b7cad375adbb0d278d9d3e04184 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Thu, 9 Feb 2017 16:20:58 -0400 Subject: [PATCH 03/90] Add relevant scripts, actions. --- js/customize-snapshots-front.js | 17 +++++++++++++++-- php/class-customize-snapshot-manager.php | 12 +++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 3eca4b2f..ba95b6f9 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -1,11 +1,13 @@ -/* global jQuery */ +/* global jQuery, ajaxurl */ /* exported CustomizeSnapshotsFront */ var CustomizeSnapshotsFront = (function( $ ) { 'use strict'; var component = { data: { - confirmationMsg: '' + confirmationMsg: '', + ajaxurl: '', + action: '' } }; /** @@ -35,6 +37,17 @@ var CustomizeSnapshotsFront = (function( $ ) { if ( ! confirm( component.data.confirmationMsg ) ) { return false; } + $.ajax({ + url: component.data.ajaxurl, + type: 'POST', + dataType: 'json', + data: { + action: component.data.action, + snapshotsFrontendPublishNonce: component.data.action + } + } ).done( function( resp ) { + window.location = e.target.href; + } ); return true; } ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 5d4cfe26..d0e09f50 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -98,6 +98,7 @@ function hooks() { add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); + add_action( 'wp_ajax_customize-snapshots-frontend-publish', array( $this, 'snapshot_frontend_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -347,7 +348,9 @@ public function enqueue_frontend_scripts() { wp_enqueue_script( $handle ); $exports = array( 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), - 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'snapshots-frontend-publish' ), + 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), + 'action' => 'customize-snapshots-frontend-publish', + 'ajaxurl' => admin_url( 'admin-ajax.php' ), ); wp_add_inline_script( $handle, @@ -970,4 +973,11 @@ public function get_front_uuid_param() { public function get_customize_uuid_param() { return constant( get_class( $this->post_type ) . '::CUSTOMIZE_UUID_PARAM_NAME' ); } + + public function snapshot_frontend_publish() { + // if ( ! check_ajax_referer( ) ) + // @todo create the publishing logic. + + wp_send_json_success(); + } } From 7f214b93a0bdf76c7f36b54f82abbf465b6775bc Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Fri, 10 Feb 2017 12:49:44 -0400 Subject: [PATCH 04/90] Add publishing logic. Use wp.ajax.post. --- js/customize-snapshots-front.js | 31 +++++++++++++++--------- php/class-customize-snapshot-manager.php | 31 ++++++++++++++++++++---- php/class-plugin.php | 2 +- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index ba95b6f9..483e204d 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -1,4 +1,4 @@ -/* global jQuery, ajaxurl */ +/* global jQuery, wp */ /* exported CustomizeSnapshotsFront */ var CustomizeSnapshotsFront = (function( $ ) { 'use strict'; @@ -6,8 +6,8 @@ var CustomizeSnapshotsFront = (function( $ ) { var component = { data: { confirmationMsg: '', - ajaxurl: '', - action: '' + action: '', + snapshotsFrontendPublishNonce: '' } }; /** @@ -24,6 +24,11 @@ var CustomizeSnapshotsFront = (function( $ ) { } ); }; + /** + * Set up snapshot frontend publish button. + * + * @returns {void} + */ component.setupPublishButton = function setupPublishButton() { var publishBtn = $( '#wp-admin-bar-publish-customize-snapshot a' ); @@ -32,21 +37,23 @@ var CustomizeSnapshotsFront = (function( $ ) { } publishBtn.click( function( e ) { + var request; e.preventDefault(); if ( ! confirm( component.data.confirmationMsg ) ) { return false; } - $.ajax({ - url: component.data.ajaxurl, - type: 'POST', - dataType: 'json', - data: { - action: component.data.action, - snapshotsFrontendPublishNonce: component.data.action + request = wp.ajax.post( component.data.action, { + nonce: component.data.snapshotsFrontendPublishNonce, + uuid: component.data.uuid + } ); + request.done( function( data ) { + if ( data && data.success ){ + window.location = e.target.href; // @todo Redirect to correct URL. } - } ).done( function( resp ) { - window.location = e.target.href; + } ); + request.fail( function( data ) { + alert( data.errorMsg ); // @todo Maybe tune the way of failure notice. } ); return true; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index d0e09f50..fb280898 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -350,7 +350,7 @@ public function enqueue_frontend_scripts() { 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), 'action' => 'customize-snapshots-frontend-publish', - 'ajaxurl' => admin_url( 'admin-ajax.php' ), + 'uuid' => $this->snapshot->uuid(), ); wp_add_inline_script( $handle, @@ -974,10 +974,31 @@ public function get_customize_uuid_param() { return constant( get_class( $this->post_type ) . '::CUSTOMIZE_UUID_PARAM_NAME' ); } + /** + * Publishes changeset from frontend. + */ public function snapshot_frontend_publish() { - // if ( ! check_ajax_referer( ) ) - // @todo create the publishing logic. - - wp_send_json_success(); + if ( ! check_ajax_referer( 'customize-snapshots-frontend-publish', 'nonce' ) ) { + status_header( 400 ); + wp_send_json_error( 'bad_nonce' ); + } + + if ( ! isset( $_POST['uuid'] ) ) { + return; + } + $this->current_snapshot_uuid = esc_attr( $_POST['uuid'] ); + $this->ensure_customize_manager(); + $r = $this->customize_manager->save_changeset_post( array( + 'status' => 'publish', + ) ); + if ( is_wp_error( $r ) ) { + $msg = __( 'Publishing failed: ', 'customize-snapshots' ); + foreach( $r->errors as $name => $value ){ + $msg .= $name . '; '; + } + wp_send_json_error( array( 'errorMsg' => $msg ) ); + } else { + wp_send_json_success( array( 'success' => true ) ); + } } } diff --git a/php/class-plugin.php b/php/class-plugin.php index 833cf55d..1f06e1c3 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -132,7 +132,7 @@ public function register_scripts( \WP_Scripts $wp_scripts ) { $handle = 'customize-snapshots-front'; $src = $this->dir_url . 'js/customize-snapshots-front' . $min . '.js'; - $deps = array( 'jquery', 'underscore' ); + $deps = array( 'jquery', 'wp-backbone', 'underscore' ); $wp_scripts->add( $handle, $src, $deps ); } From 24840c86019a48cf39fc94c7df7fa899659603fe Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Tue, 14 Feb 2017 12:18:43 -0400 Subject: [PATCH 05/90] Remove todos. --- js/customize-snapshots-front.js | 7 ++++--- php/class-customize-snapshot-manager.php | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 483e204d..8e32e962 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -7,7 +7,8 @@ var CustomizeSnapshotsFront = (function( $ ) { data: { confirmationMsg: '', action: '', - snapshotsFrontendPublishNonce: '' + snapshotsFrontendPublishNonce: '', + errorMsg: '' } }; /** @@ -49,11 +50,11 @@ var CustomizeSnapshotsFront = (function( $ ) { } ); request.done( function( data ) { if ( data && data.success ){ - window.location = e.target.href; // @todo Redirect to correct URL. + window.location = e.target.href; } } ); request.fail( function( data ) { - alert( data.errorMsg ); // @todo Maybe tune the way of failure notice. + alert( data.errorMsg ); } ); return true; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index fb280898..d9131209 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -986,11 +986,13 @@ public function snapshot_frontend_publish() { if ( ! isset( $_POST['uuid'] ) ) { return; } + $this->current_snapshot_uuid = esc_attr( $_POST['uuid'] ); $this->ensure_customize_manager(); $r = $this->customize_manager->save_changeset_post( array( 'status' => 'publish', ) ); + if ( is_wp_error( $r ) ) { $msg = __( 'Publishing failed: ', 'customize-snapshots' ); foreach( $r->errors as $name => $value ){ From 0edf0dee2cc8a5d10a22a05c8e2061655c4c8083 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Tue, 14 Feb 2017 12:42:06 -0400 Subject: [PATCH 06/90] Fix JSCS. --- js/customize-snapshots-front.js | 8 ++++---- php/class-customize-snapshot-manager.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 8e32e962..0aba6db9 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -41,7 +41,7 @@ var CustomizeSnapshotsFront = (function( $ ) { var request; e.preventDefault(); - if ( ! confirm( component.data.confirmationMsg ) ) { + if ( ! window.confirm( component.data.confirmationMsg ) ) { return false; } request = wp.ajax.post( component.data.action, { @@ -49,12 +49,12 @@ var CustomizeSnapshotsFront = (function( $ ) { uuid: component.data.uuid } ); request.done( function( data ) { - if ( data && data.success ){ + if ( data && data.success ) { window.location = e.target.href; } } ); request.fail( function( data ) { - alert( data.errorMsg ); + window.alert( data.errorMsg ); } ); return true; @@ -62,4 +62,4 @@ var CustomizeSnapshotsFront = (function( $ ) { }; return component; -})( jQuery ); \ No newline at end of file +})( jQuery ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index d9131209..242b2bb4 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -98,7 +98,7 @@ function hooks() { add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'wp_ajax_customize-snapshots-frontend-publish', array( $this, 'snapshot_frontend_publish' ) ); + add_action( 'wp_ajax_customize-snapshots-frontend-publish', array( $this, 'ajax_snapshot_frontend_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -977,7 +977,7 @@ public function get_customize_uuid_param() { /** * Publishes changeset from frontend. */ - public function snapshot_frontend_publish() { + public function ajax_snapshot_frontend_publish() { if ( ! check_ajax_referer( 'customize-snapshots-frontend-publish', 'nonce' ) ) { status_header( 400 ); wp_send_json_error( 'bad_nonce' ); From 8270875013f869e16b96f944869d0d6ec64dbe82 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Tue, 14 Feb 2017 12:53:30 -0400 Subject: [PATCH 07/90] Fix phpcs. Fix ESLint. --- js/customize-snapshots-front.js | 106 +++++++++++------------ php/class-customize-snapshot-manager.php | 70 +++++++-------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 0aba6db9..cdae8a8c 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -1,65 +1,65 @@ /* global jQuery, wp */ /* exported CustomizeSnapshotsFront */ var CustomizeSnapshotsFront = (function( $ ) { - 'use strict'; + 'use strict'; - var component = { - data: { - confirmationMsg: '', - action: '', - snapshotsFrontendPublishNonce: '', - errorMsg: '' - } - }; - /** - * Init. - * - * @param {object} args Args. - * @returns {void} - */ - component.init = function init( args ) { - _.extend( component.data, args ); + var component = { + data: { + confirmationMsg: '', + action: '', + snapshotsFrontendPublishNonce: '', + errorMsg: '' + } + }; + /** + * Init. + * + * @param {object} args Args. + * @returns {void} + */ + component.init = function init( args ) { + _.extend( component.data, args ); - $( document ).ready( function() { - component.setupPublishButton(); - } ); - }; + $( document ).ready( function() { + component.setupPublishButton(); + } ); + }; - /** - * Set up snapshot frontend publish button. - * - * @returns {void} - */ - component.setupPublishButton = function setupPublishButton() { - var publishBtn = $( '#wp-admin-bar-publish-customize-snapshot a' ); + /** + * Set up snapshot frontend publish button. + * + * @returns {void} + */ + component.setupPublishButton = function setupPublishButton() { + var publishBtn = $( '#wp-admin-bar-publish-customize-snapshot a' ); - if ( ! publishBtn.length ) { - return; - } + if ( ! publishBtn.length ) { + return; + } - publishBtn.click( function( e ) { - var request; - e.preventDefault(); + publishBtn.click( function( e ) { + var request; + e.preventDefault(); - if ( ! window.confirm( component.data.confirmationMsg ) ) { - return false; - } - request = wp.ajax.post( component.data.action, { - nonce: component.data.snapshotsFrontendPublishNonce, - uuid: component.data.uuid - } ); - request.done( function( data ) { - if ( data && data.success ) { - window.location = e.target.href; - } - } ); - request.fail( function( data ) { - window.alert( data.errorMsg ); - } ); + if ( ! window.confirm( component.data.confirmationMsg ) ) { // eslint-disable-line no-alert + return false; + } + request = wp.ajax.post( component.data.action, { + nonce: component.data.snapshotsFrontendPublishNonce, + uuid: component.data.uuid + } ); + request.done( function( data ) { + if ( data && data.success ) { + window.location = e.target.href; + } + } ); + request.fail( function( data ) { + window.alert( data.errorMsg ); // eslint-disable-line no-alert + } ); - return true; - } ); - }; + return true; + } ); + }; - return component; + return component; })( jQuery ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 242b2bb4..a72fb725 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -346,17 +346,17 @@ public function enqueue_frontend_scripts() { } $handle = 'customize-snapshots-front'; wp_enqueue_script( $handle ); - $exports = array( - 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), - 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), - 'action' => 'customize-snapshots-frontend-publish', - 'uuid' => $this->snapshot->uuid(), - ); - wp_add_inline_script( - $handle, - sprintf( 'CustomizeSnapshotsFront.init( %s )', wp_json_encode( $exports ) ), - 'after' - ); + $exports = array( + 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), + 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), + 'action' => 'customize-snapshots-frontend-publish', + 'uuid' => $this->snapshot->uuid(), + ); + wp_add_inline_script( + $handle, + sprintf( 'CustomizeSnapshotsFront.init( %s )', wp_json_encode( $exports ) ), + 'after' + ); } /** @@ -499,10 +499,10 @@ public function print_admin_bar_styles() { content: "\f179"; top: 2px; } - #wpadminbar #wp-admin-bar-publish-customize-snapshot > .ab-item:before { - content: "\f147"; - top: 2px; - } + #wpadminbar #wp-admin-bar-publish-customize-snapshot > .ab-item:before { + content: "\f147"; + top: 2px; + } #wpadminbar #wp-admin-bar-exit-customize-snapshot > .ab-item:before { content: "\f158"; top: 2px; @@ -640,11 +640,11 @@ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { return; } $snapshot_admin_bar_node_ids = array( - 'customize', - 'exit-customize-snapshot', - 'inspect-customize-snapshot', - 'publish-customize-snapshot', - ); + 'customize', + 'exit-customize-snapshot', + 'inspect-customize-snapshot', + 'publish-customize-snapshot', + ); foreach ( $wp_admin_bar->get_nodes() as $node ) { if ( in_array( $node->id, $snapshot_admin_bar_node_ids, true ) || '#' === substr( $node->href, 0, 1 ) ) { continue; @@ -978,14 +978,14 @@ public function get_customize_uuid_param() { * Publishes changeset from frontend. */ public function ajax_snapshot_frontend_publish() { - if ( ! check_ajax_referer( 'customize-snapshots-frontend-publish', 'nonce' ) ) { - status_header( 400 ); - wp_send_json_error( 'bad_nonce' ); - } + if ( ! check_ajax_referer( 'customize-snapshots-frontend-publish', 'nonce' ) ) { + status_header( 400 ); + wp_send_json_error( 'bad_nonce' ); + } - if ( ! isset( $_POST['uuid'] ) ) { - return; - } + if ( ! isset( $_POST['uuid'] ) ) { + return; + } $this->current_snapshot_uuid = esc_attr( $_POST['uuid'] ); $this->ensure_customize_manager(); @@ -994,13 +994,13 @@ public function ajax_snapshot_frontend_publish() { ) ); if ( is_wp_error( $r ) ) { - $msg = __( 'Publishing failed: ', 'customize-snapshots' ); - foreach( $r->errors as $name => $value ){ - $msg .= $name . '; '; - } - wp_send_json_error( array( 'errorMsg' => $msg ) ); - } else { + $msg = __( 'Publishing failed: ', 'customize-snapshots' ); + foreach ( $r->errors as $name => $value ) { + $msg .= $name . '; '; + } + wp_send_json_error( array( 'errorMsg' => $msg ) ); + } else { wp_send_json_success( array( 'success' => true ) ); - } - } + } + } } From 10ca219e691bde583f1251f39a7028245c4bf29e Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Wed, 15 Feb 2017 10:29:15 -0400 Subject: [PATCH 08/90] Permission fixes. --- js/customize-snapshots-front.js | 4 +++- php/class-customize-snapshot-manager.php | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index cdae8a8c..8cf68162 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -54,7 +54,9 @@ var CustomizeSnapshotsFront = (function( $ ) { } } ); request.fail( function( data ) { - window.alert( data.errorMsg ); // eslint-disable-line no-alert + if ( data && data.errorMsg ) { + window.alert( data.errorMsg ); // eslint-disable-line no-alert + } } ); return true; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index a72fb725..0b28321b 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -341,13 +341,14 @@ public function enqueue_admin_scripts( $hook ) { * These files control the behavior frontend. */ public function enqueue_frontend_scripts() { - if ( ! $this->snapshot ) { + if ( ! $this->snapshot || ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { return; } + $handle = 'customize-snapshots-front'; wp_enqueue_script( $handle ); $exports = array( - 'confirmationMsg' => __( 'Are you sure that you want to publish the Snapshot?', 'customize-snapshots' ), + 'confirmationMsg' => __( 'Are you sure that you want to publish the Changeset?', 'customize-snapshots' ), 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), 'action' => 'customize-snapshots-frontend-publish', 'uuid' => $this->snapshot->uuid(), @@ -594,7 +595,7 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_publish_snapshot_link( $wp_admin_bar ) { - if ( ! $this->snapshot ) { + if ( ! $this->snapshot || ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { return; } $post = $this->snapshot->post(); @@ -603,7 +604,7 @@ public function add_publish_snapshot_link( $wp_admin_bar ) { } $wp_admin_bar->add_menu( array( 'id' => 'publish-customize-snapshot', - 'title' => __( 'Publish Snapshot', 'customize-snapshots' ), + 'title' => __( 'Publish Changeset', 'customize-snapshots' ), 'href' => remove_query_arg( $this->get_front_uuid_param() ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', @@ -983,11 +984,16 @@ public function ajax_snapshot_frontend_publish() { wp_send_json_error( 'bad_nonce' ); } + if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { + status_header( 403 ); + wp_send_json_error( 'insufficient_post_permissions' ); + } + if ( ! isset( $_POST['uuid'] ) ) { - return; + wp_send_json_error(); } - $this->current_snapshot_uuid = esc_attr( $_POST['uuid'] ); + $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_POST['uuid'] ) ); $this->ensure_customize_manager(); $r = $this->customize_manager->save_changeset_post( array( 'status' => 'publish', @@ -995,9 +1001,7 @@ public function ajax_snapshot_frontend_publish() { if ( is_wp_error( $r ) ) { $msg = __( 'Publishing failed: ', 'customize-snapshots' ); - foreach ( $r->errors as $name => $value ) { - $msg .= $name . '; '; - } + $msg .= join( "; ", array_keys( $r->errors ) ); wp_send_json_error( array( 'errorMsg' => $msg ) ); } else { wp_send_json_success( array( 'success' => true ) ); From 62bba49db59ad8f1befb413c064ee8f939235e60 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Wed, 15 Feb 2017 10:31:16 -0400 Subject: [PATCH 09/90] Fix indent. --- js/customize-snapshots-front.js | 2 +- php/class-customize-snapshot-manager.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 8cf68162..0748d953 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -55,7 +55,7 @@ var CustomizeSnapshotsFront = (function( $ ) { } ); request.fail( function( data ) { if ( data && data.errorMsg ) { - window.alert( data.errorMsg ); // eslint-disable-line no-alert + window.alert( data.errorMsg ); // eslint-disable-line no-alert } } ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 0b28321b..1d5a37fb 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -990,7 +990,7 @@ public function ajax_snapshot_frontend_publish() { } if ( ! isset( $_POST['uuid'] ) ) { - wp_send_json_error(); + wp_send_json_error(); } $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_POST['uuid'] ) ); From 472182db326c6a188c4401b41550ebe2853ff635 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Wed, 15 Feb 2017 10:33:34 -0400 Subject: [PATCH 10/90] Fix phpcs. --- 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 1d5a37fb..8cf0a98d 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -1001,7 +1001,7 @@ public function ajax_snapshot_frontend_publish() { if ( is_wp_error( $r ) ) { $msg = __( 'Publishing failed: ', 'customize-snapshots' ); - $msg .= join( "; ", array_keys( $r->errors ) ); + $msg .= join( '; ', array_keys( $r->errors ) ); wp_send_json_error( array( 'errorMsg' => $msg ) ); } else { wp_send_json_success( array( 'success' => true ) ); From f42969cea7ca1981725160e82592b21f632b2372 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Wed, 15 Feb 2017 12:54:46 -0400 Subject: [PATCH 11/90] Add support for theme change. --- js/customize-snapshots-front.js | 25 ++++++++++++++---------- php/class-customize-snapshot-manager.php | 25 ++++++++++++++++-------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/js/customize-snapshots-front.js b/js/customize-snapshots-front.js index 0748d953..40601945 100644 --- a/js/customize-snapshots-front.js +++ b/js/customize-snapshots-front.js @@ -38,24 +38,29 @@ var CustomizeSnapshotsFront = (function( $ ) { } publishBtn.click( function( e ) { - var request; + var request, + data = { + nonce: component.data.snapshotsFrontendPublishNonce, + uuid: component.data.uuid + }; e.preventDefault(); if ( ! window.confirm( component.data.confirmationMsg ) ) { // eslint-disable-line no-alert return false; } - request = wp.ajax.post( component.data.action, { - nonce: component.data.snapshotsFrontendPublishNonce, - uuid: component.data.uuid - } ); - request.done( function( data ) { - if ( data && data.success ) { + if ( ! wp.customize.settings.theme.active ) { + data.stylesheet = wp.customize.settings.theme.stylesheet; + } + request = wp.ajax.post( component.data.action, data ); + + request.done( function( resp ) { + if ( resp && resp.success ) { window.location = e.target.href; } } ); - request.fail( function( data ) { - if ( data && data.errorMsg ) { - window.alert( data.errorMsg ); // eslint-disable-line no-alert + request.fail( function( resp ) { + if ( resp && resp.errorMsg ) { + window.alert( resp.errorMsg ); // eslint-disable-line no-alert } } ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 8cf0a98d..4bc5d495 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -98,7 +98,7 @@ function hooks() { add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'wp_ajax_customize-snapshots-frontend-publish', array( $this, 'ajax_snapshot_frontend_publish' ) ); + add_action( 'wp_ajax_customize_snapshots_frontend_publish', array( $this, 'ajax_snapshot_frontend_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -183,16 +183,16 @@ public function doing_customize_save_ajax() { * Ensure Customizer manager is instantiated. * * @global \WP_Customize_Manager $wp_customize + * @param array $args Array of params. */ - public function ensure_customize_manager() { + public function ensure_customize_manager( $args = array() ) { global $wp_customize; if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); if ( null !== $this->current_snapshot_uuid ) { - $wp_customize = new \WP_Customize_Manager( array( 'changeset_uuid' => $this->current_snapshot_uuid ) ); // WPCS: override ok. - } else { - $wp_customize = new \WP_Customize_Manager(); // WPCS: override ok. + $args['changeset_uuid'] = $this->current_snapshot_uuid; } + $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. } $this->customize_manager = $wp_customize; @@ -350,7 +350,7 @@ public function enqueue_frontend_scripts() { $exports = array( 'confirmationMsg' => __( 'Are you sure that you want to publish the Changeset?', 'customize-snapshots' ), 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), - 'action' => 'customize-snapshots-frontend-publish', + 'action' => 'customize_snapshots_frontend_publish', 'uuid' => $this->snapshot->uuid(), ); wp_add_inline_script( @@ -605,7 +605,11 @@ public function add_publish_snapshot_link( $wp_admin_bar ) { $wp_admin_bar->add_menu( array( 'id' => 'publish-customize-snapshot', 'title' => __( 'Publish Changeset', 'customize-snapshots' ), - 'href' => remove_query_arg( $this->get_front_uuid_param() ), + 'href' => remove_query_arg( array( + $this->get_front_uuid_param(), + 'theme', + $this->get_customize_uuid_param(), + ) ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', ), @@ -994,7 +998,12 @@ public function ajax_snapshot_frontend_publish() { } $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_POST['uuid'] ) ); - $this->ensure_customize_manager(); + $args = array(); + if ( isset( $_POST['stylesheet'] ) ) { + $stylesheet = sanitize_text_field( wp_unslash( $_POST['stylesheet'] ) ); + $args['theme'] = $stylesheet; + } + $this->ensure_customize_manager( $args ); $r = $this->customize_manager->save_changeset_post( array( 'status' => 'publish', ) ); From c5aaa1f3a769e4a54b1326c43b2c6f7d14aade09 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Wed, 15 Feb 2017 13:49:57 -0400 Subject: [PATCH 12/90] Fix coveralls phpunit. --- php/class-customize-snapshot-manager.php | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 4bc5d495..f229b224 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -76,6 +76,14 @@ class Customize_Snapshot_Manager { */ public $original_stylesheet; + /** + * New active theme. + * + * @access public + * @var string + */ + public $stylesheet; + /** * Constructor. * @@ -183,15 +191,19 @@ public function doing_customize_save_ajax() { * Ensure Customizer manager is instantiated. * * @global \WP_Customize_Manager $wp_customize - * @param array $args Array of params. */ - public function ensure_customize_manager( $args = array() ) { + public function ensure_customize_manager() { global $wp_customize; + + $args = array(); if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); if ( null !== $this->current_snapshot_uuid ) { $args['changeset_uuid'] = $this->current_snapshot_uuid; } + if ( null !== $this->stylesheet ) { + $args['theme'] = $this->stylesheet; + } $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. } @@ -595,7 +607,7 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_publish_snapshot_link( $wp_admin_bar ) { - if ( ! $this->snapshot || ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { + if ( ! $this->snapshot ) { return; } $post = $this->snapshot->post(); @@ -998,12 +1010,10 @@ public function ajax_snapshot_frontend_publish() { } $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_POST['uuid'] ) ); - $args = array(); if ( isset( $_POST['stylesheet'] ) ) { - $stylesheet = sanitize_text_field( wp_unslash( $_POST['stylesheet'] ) ); - $args['theme'] = $stylesheet; + $this->stylesheet = sanitize_text_field( wp_unslash( $_POST['stylesheet'] ) ); } - $this->ensure_customize_manager( $args ); + $this->ensure_customize_manager(); $r = $this->customize_manager->save_changeset_post( array( 'status' => 'publish', ) ); From 93c91e106ff962c64127560fec92c43b85982a70 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Mon, 20 Feb 2017 14:33:43 -0400 Subject: [PATCH 13/90] Change Snapshot to Changeset. --- 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 f229b224..d71524d2 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -602,7 +602,7 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { } /** - * Adds a "Publish Snapshot" link to the Toolbar when in Snapshot mode. + * Adds a "Publish Changeset" link to the Toolbar when in Snapshot mode. * * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ From 3bb9552d4cd303d99b112c203f9be1d2e188fa76 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 26 Jul 2017 19:41:03 -0700 Subject: [PATCH 14/90] Add paragraph break in readme --- readme.md | 4 ++-- readme.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 0a9186f5..2913b173 100644 --- a/readme.md +++ b/readme.md @@ -15,8 +15,8 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts ## Description ## -Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. -The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. +Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. + While the plugin's technical infrastructure for changesets was merged in WordPress 4.7,the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. For a rundown of all the features, see the screenshots below as well as the 0.6 release video: diff --git a/readme.txt b/readme.txt index e75fe574..be5dce14 100644 --- a/readme.txt +++ b/readme.txt @@ -11,8 +11,8 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts == Description == -Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. -The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. +Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. + While the plugin's technical infrastructure for changesets was merged in WordPress 4.7,the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. For a rundown of all the features, see the screenshots below as well as the 0.6 release video: From 369b4ead7b634ab6ccafc38929af49611af9bbb1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 26 Jul 2017 19:51:39 -0700 Subject: [PATCH 15/90] Add missing space in readme --- readme.md | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 2913b173..f9cb2bb2 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,7 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. -While the plugin's technical infrastructure for changesets was merged in WordPress 4.7,the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. +While the plugin's technical infrastructure for changesets was merged in WordPress 4.7, the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. For a rundown of all the features, see the screenshots below as well as the 0.6 release video: diff --git a/readme.txt b/readme.txt index be5dce14..88b6d52e 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts Customize Snapshots is the feature plugin which prototyped [Customizer changesets](https://make.wordpress.org/core/2016/10/12/customize-changesets-technical-design-decisions/); this feature was [merged](https://make.wordpress.org/core/2016/10/12/customize-changesets-formerly-transactions-merge-proposal/) as part of WordPress 4.7. The term “snapshots” was chosen because the Customizer feature revolved around saving the state (taking a snapshot) of the Customizer at a given time so that the changes could be saved as a draft and scheduled for future publishing. -While the plugin's technical infrastructure for changesets was merged in WordPress 4.7,the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. +While the plugin's technical infrastructure for changesets was merged in WordPress 4.7, the user interface still remains largely in the Customize Snapshots plugin, in which we will continue to iterate and prototype features to merge into core. For a rundown of all the features, see the screenshots below as well as the 0.6 release video: From a1ef15547341a9ee058ca4a41ab8032e92d45576 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Thu, 27 Jul 2017 14:56:40 +0300 Subject: [PATCH 16/90] Remove changeset from storage after publishing. --- js/customize-snapshots-frontend.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index aaa50e04..8e61a337 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -137,6 +137,7 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { request.done( function( resp ) { if ( resp && resp.success ) { + sessionStorage.removeItem( 'customize_changeset_uuid' ); window.location = e.target.href; } } ); From b6fb4e368241fae23a026cd86e5f841555aa0df9 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Mon, 31 Jul 2017 17:50:22 +0300 Subject: [PATCH 17/90] Rework publishing changeset to use wp_nonce_url instead of AJAX. --- js/customize-snapshots-frontend.js | 25 +----------- php/class-customize-snapshot-manager.php | 49 +++++++++++++++--------- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index 8e61a337..dbe083f1 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -120,33 +120,10 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { } publishBtn.click( function( e ) { - var request, - data = { - nonce: component.data.snapshotsFrontendPublishNonce, - uuid: component.data.uuid - }; - e.preventDefault(); - if ( ! window.confirm( component.data.confirmationMsg ) ) { // eslint-disable-line no-alert return false; } - if ( ! wp.customize.settings.theme.active ) { - data.stylesheet = wp.customize.settings.theme.stylesheet; - } - request = wp.ajax.post( component.data.action, data ); - - request.done( function( resp ) { - if ( resp && resp.success ) { - sessionStorage.removeItem( 'customize_changeset_uuid' ); - window.location = e.target.href; - } - } ); - request.fail( function( resp ) { - if ( resp && resp.errorMsg ) { - window.alert( resp.errorMsg ); // eslint-disable-line no-alert - } - } ); - + sessionStorage.removeItem( 'customize_changeset_uuid' ); return true; } ); }; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 68103198..7c33d302 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -108,6 +108,7 @@ function hooks() { add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); add_action( 'wp_ajax_customize_snapshots_frontend_publish', array( $this, 'ajax_snapshot_frontend_publish' ) ); + add_action( 'load-edit.php', array( $this, 'snapshot_frontend_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -674,14 +675,17 @@ public function add_publish_snapshot_link( $wp_admin_bar ) { if ( ! $post ) { return; } + + $href = "edit.php?post_type=customize_changeset&action=publish&uuid=$this->current_snapshot_uuid"; + + if ( isset( $_GET['customize_theme'] ) ) { + $href .= '&stylesheet=' . sanitize_text_field( wp_unslash( $_GET['customize_theme'] ) ); + } + $wp_admin_bar->add_menu( array( 'id' => 'publish-customize-snapshot', 'title' => __( 'Publish Changeset', 'customize-snapshots' ), - 'href' => remove_query_arg( array( - $this->get_front_uuid_param(), - 'theme', - $this->get_customize_uuid_param(), - ) ), + 'href' => admin_url( wp_nonce_url( $href, 'publish-changeset_' . $this->current_snapshot_uuid ) ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', ), @@ -1056,24 +1060,22 @@ public function get_customize_uuid_param() { /** * Publishes changeset from frontend. */ - public function ajax_snapshot_frontend_publish() { - if ( ! check_ajax_referer( 'customize-snapshots-frontend-publish', 'nonce' ) ) { - status_header( 400 ); - wp_send_json_error( 'bad_nonce' ); - } + public function snapshot_frontend_publish() { if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { - status_header( 403 ); - wp_send_json_error( 'insufficient_post_permissions' ); + wp_die( 'insufficient_post_permissions', 403 ); } - if ( ! isset( $_POST['uuid'] ) ) { - wp_send_json_error(); + if ( ! isset( $_GET['uuid'] ) ) { + wp_die( __( 'UUID missing.' ), 403 ); } + $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); + + check_admin_referer( 'publish-changeset_' . $this->current_snapshot_uuid ); + - $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_POST['uuid'] ) ); - if ( isset( $_POST['stylesheet'] ) ) { - $this->stylesheet = sanitize_text_field( wp_unslash( $_POST['stylesheet'] ) ); + if ( isset( $_GET['stylesheet'] ) ) { + $this->stylesheet = sanitize_text_field( wp_unslash( $_GET['stylesheet'] ) ); } $this->ensure_customize_manager(); $r = $this->customize_manager->save_changeset_post( array( @@ -1083,9 +1085,18 @@ public function ajax_snapshot_frontend_publish() { if ( is_wp_error( $r ) ) { $msg = __( 'Publishing failed: ', 'customize-snapshots' ); $msg .= join( '; ', array_keys( $r->errors ) ); - wp_send_json_error( array( 'errorMsg' => $msg ) ); + wp_die( + '

' . $msg . '

', + 403 + ); } else { - wp_send_json_success( array( 'success' => true ) ); + $sendback = remove_query_arg( array( + $this->get_front_uuid_param(), + 'customize_theme', + $this->get_customize_uuid_param(), + ), wp_get_referer() ); + wp_redirect( $sendback ); + exit(); } } From b2ba684d156dcd76c3bd66e849e09c3f2bc9311a Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Mon, 31 Jul 2017 17:56:42 +0300 Subject: [PATCH 18/90] Phpcs fixes. --- js/customize-snapshots-frontend.js | 2 +- php/class-customize-snapshot-manager.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index dbe083f1..eaa6d7df 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -119,7 +119,7 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { return; } - publishBtn.click( function( e ) { + publishBtn.click( function() { if ( ! window.confirm( component.data.confirmationMsg ) ) { // eslint-disable-line no-alert return false; } diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 7c33d302..f51aeb66 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -1067,13 +1067,12 @@ public function snapshot_frontend_publish() { } if ( ! isset( $_GET['uuid'] ) ) { - wp_die( __( 'UUID missing.' ), 403 ); + wp_die( __( 'UUID missing.', 'customize-snapshots' ), 403 ); } $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); check_admin_referer( 'publish-changeset_' . $this->current_snapshot_uuid ); - if ( isset( $_GET['stylesheet'] ) ) { $this->stylesheet = sanitize_text_field( wp_unslash( $_GET['stylesheet'] ) ); } From 2aaf97735d1dbb245d465681c430aeebea26e756 Mon Sep 17 00:00:00 2001 From: Miina Sikk Date: Mon, 31 Jul 2017 18:20:08 +0300 Subject: [PATCH 19/90] Fix logic for non-action cases. --- php/class-customize-snapshot-manager.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index f51aeb66..20a664f8 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -1062,14 +1062,14 @@ public function get_customize_uuid_param() { */ public function snapshot_frontend_publish() { - if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { - wp_die( 'insufficient_post_permissions', 403 ); + if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'publish' !== $_GET['action'] ) { + return; } + $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); - if ( ! isset( $_GET['uuid'] ) ) { - wp_die( __( 'UUID missing.', 'customize-snapshots' ), 403 ); + if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { + wp_die( 'insufficient_post_permissions', 403 ); } - $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); check_admin_referer( 'publish-changeset_' . $this->current_snapshot_uuid ); From 4fd7832fe9d378a1847638db695726ce5dbac57e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 1 Aug 2017 17:26:47 -0700 Subject: [PATCH 20/90] Remove obsolete ajax code; ensure valid redirect; improve query param handling --- js/customize-snapshots-frontend.js | 5 +-- php/class-customize-snapshot-manager.php | 45 ++++++++++++++++-------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index eaa6d7df..904d7320 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -12,10 +12,7 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { l10n: { restoreSessionPrompt: '' }, - confirmationMsg: '', - action: '', - snapshotsFrontendPublishNonce: '', - errorMsg: '' + confirmationMsg: '' } }; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 20a664f8..2ace95d0 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -107,7 +107,6 @@ function hooks() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'wp_ajax_customize_snapshots_frontend_publish', array( $this, 'ajax_snapshot_frontend_publish' ) ); add_action( 'load-edit.php', array( $this, 'snapshot_frontend_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); @@ -376,8 +375,6 @@ public function enqueue_frontend_scripts() { 'restoreSessionPrompt' => __( 'It seems you may have inadvertently navigated away from previewing a customized state. Would you like to restore the changeset context?', 'customize-snapshots' ), ), 'confirmationMsg' => __( 'Are you sure that you want to publish the Changeset?', 'customize-snapshots' ), - 'snapshotsFrontendPublishNonce' => wp_create_nonce( 'customize-snapshots-frontend-publish' ), - 'action' => 'customize_snapshots_frontend_publish', ); wp_add_inline_script( $handle, @@ -676,16 +673,19 @@ public function add_publish_snapshot_link( $wp_admin_bar ) { return; } - $href = "edit.php?post_type=customize_changeset&action=publish&uuid=$this->current_snapshot_uuid"; - - if ( isset( $_GET['customize_theme'] ) ) { - $href .= '&stylesheet=' . sanitize_text_field( wp_unslash( $_GET['customize_theme'] ) ); - } - + $href = add_query_arg( + array( + 'post_type' => 'customize_changeset', + 'action' => 'frontend_publish', + 'uuid' => $this->current_snapshot_uuid, + 'stylesheet' => get_stylesheet(), + ), + admin_url( 'edit.php' ) + ); $wp_admin_bar->add_menu( array( 'id' => 'publish-customize-snapshot', 'title' => __( 'Publish Changeset', 'customize-snapshots' ), - 'href' => admin_url( wp_nonce_url( $href, 'publish-changeset_' . $this->current_snapshot_uuid ) ), + 'href' => wp_nonce_url( $href, 'publish-changeset_' . $this->current_snapshot_uuid ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', ), @@ -1062,10 +1062,14 @@ public function get_customize_uuid_param() { */ public function snapshot_frontend_publish() { - if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'publish' !== $_GET['action'] ) { + if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'frontend_publish' !== $_GET['action'] ) { + return; + } + $uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); + if ( ! static::is_valid_uuid( $uuid ) ) { return; } - $this->current_snapshot_uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); + $this->current_snapshot_uuid = $uuid; if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { wp_die( 'insufficient_post_permissions', 403 ); @@ -1074,8 +1078,13 @@ public function snapshot_frontend_publish() { check_admin_referer( 'publish-changeset_' . $this->current_snapshot_uuid ); if ( isset( $_GET['stylesheet'] ) ) { - $this->stylesheet = sanitize_text_field( wp_unslash( $_GET['stylesheet'] ) ); + $theme = wp_get_theme( wp_unslash( $_GET['stylesheet'] ) ); + if ( $theme->errors() ) { + wp_die( 'invalid_theme', 400 ); + } + $this->stylesheet = $theme->get_stylesheet(); } + $this->ensure_customize_manager(); $r = $this->customize_manager->save_changeset_post( array( 'status' => 'publish', @@ -1089,11 +1098,19 @@ public function snapshot_frontend_publish() { 403 ); } else { + $referer = wp_get_referer(); + + // Ensure redirect is set to frontend. + if ( empty( $referer ) || false !== strpos( parse_url( $referer, PHP_URL_PATH ), '/wp-admin/' ) ) { + $referer = home_url(); + } + $sendback = remove_query_arg( array( $this->get_front_uuid_param(), 'customize_theme', $this->get_customize_uuid_param(), - ), wp_get_referer() ); + ), $referer ); + wp_redirect( $sendback ); exit(); } From 52b5c3ee8d093823fd8aa7075b8ae69bef49dba2 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 1 Aug 2017 18:47:11 -0700 Subject: [PATCH 21/90] Update Travis to use the precise distro --- .travis.yml | 1 + dev-lib | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 70c258f2..9a461a9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ sudo: false +dist: precise notifications: email: diff --git a/dev-lib b/dev-lib index 24626547..8473c072 160000 --- a/dev-lib +++ b/dev-lib @@ -1 +1 @@ -Subproject commit 2462654769ad18e49ed835418104818ba5daed99 +Subproject commit 8473c072ddde7d478aa5316f884c4a29ef0372b0 From 72b86faf76fa589e6ecf0a9a56c69298e291ff59 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 18:44:45 -0700 Subject: [PATCH 22/90] Prevent changeset session remembrance when browsing in preview iframe --- js/customize-snapshots-frontend.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index 23ed8e09..63495a31 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -75,7 +75,8 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { * @returns {void} */ component.rememberSessionSnapshot = function rememberSessionSnapshot() { - if ( ! component.hasSessionStorage || ! component.data.uuid ) { + var isCustomizerFramePreview = /(^|\?|&)customize_messenger_channel=/.test( location.search ); + if ( ! component.hasSessionStorage || ! component.data.uuid || isCustomizerFramePreview ) { return; } sessionStorage.setItem( 'customize_changeset_uuid', component.data.uuid ); From d1019c6d8040269e73cd33ca7ece51fb35ed8592 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 21:11:17 -0700 Subject: [PATCH 23/90] Rename 'Edit' post row action to 'Inspect' --- php/class-post-type.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/php/class-post-type.php b/php/class-post-type.php index 7398fd1b..060beced 100644 --- a/php/class-post-type.php +++ b/php/class-post-type.php @@ -333,16 +333,17 @@ public function filter_post_row_actions( $actions, $post ) { ), $actions ); - } else { - if ( isset( $actions['edit'] ) ) { - $actions['edit'] = sprintf( - '%s', - get_edit_post_link( $post->ID, 'display' ), - /* translators: %s: post title */ - esc_attr( sprintf( __( 'View “%s”', 'customize-snapshots' ), get_the_title( $post->ID ) ) ), - __( 'View', 'customize-snapshots' ) - ); - } + } + + // Rename "Edit" to "Inspect" for the row action. + if ( isset( $actions['edit'] ) ) { + $actions['edit'] = sprintf( + '%s', + get_edit_post_link( $post->ID, 'display' ), + /* translators: %s: post title */ + esc_attr( sprintf( __( 'Inspect “%s”', 'customize-snapshots' ), get_the_title( $post->ID ) ) ), + __( 'Inspect', 'customize-snapshots' ) + ); } unset( $actions['inline hide-if-no-js'] ); From 8dbccab515dcb403fa37f706384162f585304db6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 21:20:16 -0700 Subject: [PATCH 24/90] Rename references from snapshots to changesets --- js/customize-snapshots-frontend.js | 4 ++-- php/class-customize-snapshot-manager.php | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/js/customize-snapshots-frontend.js b/js/customize-snapshots-frontend.js index 904d7320..9e0a044b 100644 --- a/js/customize-snapshots-frontend.js +++ b/js/customize-snapshots-frontend.js @@ -105,12 +105,12 @@ var CustomizeSnapshotsFrontend = ( function( $ ) { }; /** - * Set up snapshot frontend publish button. + * Set up changesets frontend publish button in admin bar. * * @returns {void} */ component.setupPublishButton = function setupPublishButton() { - var publishBtn = $( '#wp-admin-bar-publish-customize-snapshot a' ); + var publishBtn = $( '#wp-admin-bar-publish-customize-changeset a' ); if ( ! publishBtn.length ) { return; diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 2ace95d0..438cac05 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -107,7 +107,7 @@ function hooks() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'load-edit.php', array( $this, 'snapshot_frontend_publish' ) ); + add_action( 'load-edit.php', array( $this, 'handle_frontend_changset_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -518,7 +518,7 @@ public function customize_menu( $wp_admin_bar ) { $this->add_changesets_admin_bar_link( $wp_admin_bar ); $this->add_resume_snapshot_link( $wp_admin_bar ); $this->add_post_edit_screen_link( $wp_admin_bar ); - $this->add_publish_snapshot_link( $wp_admin_bar ); + $this->add_publish_changeset_link( $wp_admin_bar ); $this->add_snapshot_exit_link( $wp_admin_bar ); } @@ -540,7 +540,7 @@ public function print_admin_bar_styles() { content: "\f179"; top: 2px; } - #wpadminbar #wp-admin-bar-publish-customize-snapshot > .ab-item:before { + #wpadminbar #wp-admin-bar-publish-customize-changeset > .ab-item:before { content: "\f147"; top: 2px; } @@ -664,7 +664,7 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { * * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ - public function add_publish_snapshot_link( $wp_admin_bar ) { + public function add_publish_changeset_link( $wp_admin_bar ) { if ( ! $this->snapshot ) { return; } @@ -683,7 +683,7 @@ public function add_publish_snapshot_link( $wp_admin_bar ) { admin_url( 'edit.php' ) ); $wp_admin_bar->add_menu( array( - 'id' => 'publish-customize-snapshot', + 'id' => 'publish-customize-changeset', 'title' => __( 'Publish Changeset', 'customize-snapshots' ), 'href' => wp_nonce_url( $href, 'publish-changeset_' . $this->current_snapshot_uuid ), 'meta' => array( @@ -724,7 +724,7 @@ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { 'customize', 'exit-customize-snapshot', 'inspect-customize-snapshot', - 'publish-customize-snapshot', + 'publish-customize-changeset', ); foreach ( $wp_admin_bar->get_nodes() as $node ) { if ( in_array( $node->id, $snapshot_admin_bar_node_ids, true ) || '#' === substr( $node->href, 0, 1 ) ) { @@ -1058,9 +1058,9 @@ public function get_customize_uuid_param() { } /** - * Publishes changeset from frontend. + * Handles request to publish changeset from frontend. */ - public function snapshot_frontend_publish() { + public function handle_frontend_changset_publish() { if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'frontend_publish' !== $_GET['action'] ) { return; From 9a424cac9ada6261376da1ea1fcf2a2fcfbecff2 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 21:33:59 -0700 Subject: [PATCH 25/90] Improve handling of error scenarios when publishing from frontend --- php/class-customize-snapshot-manager.php | 29 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 438cac05..45c7d50e 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -1075,12 +1075,28 @@ public function handle_frontend_changset_publish() { wp_die( 'insufficient_post_permissions', 403 ); } - check_admin_referer( 'publish-changeset_' . $this->current_snapshot_uuid ); + if ( ! check_ajax_referer( 'publish-changeset_' . $this->current_snapshot_uuid, false, false ) ) { + wp_die( + esc_html__( 'Oops. Unable to publish the changeset due to an expired user session. Please go back, reload the page, and try publishing again.', 'customize-snapshots' ), + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + ) + ); + } if ( isset( $_GET['stylesheet'] ) ) { $theme = wp_get_theme( wp_unslash( $_GET['stylesheet'] ) ); if ( $theme->errors() ) { - wp_die( 'invalid_theme', 400 ); + $msg = __( 'Oops. Unable to publish the changeset. The following error(s) occurred: ', 'customize-snapshots' ); + $msg .= join( '; ', array_keys( $theme->errors()->errors ) ); + wp_die( + '

' . esc_html( $msg ) . '

', + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + ) + ); } $this->stylesheet = $theme->get_stylesheet(); } @@ -1091,11 +1107,14 @@ public function handle_frontend_changset_publish() { ) ); if ( is_wp_error( $r ) ) { - $msg = __( 'Publishing failed: ', 'customize-snapshots' ); + $msg = __( 'Oops. Unable to publish the changeset. The following error(s) occurred: ', 'customize-snapshots' ); $msg .= join( '; ', array_keys( $r->errors ) ); wp_die( - '

' . $msg . '

', - 403 + '

' . esc_html( $msg ) . '

', + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + ) ); } else { $referer = wp_get_referer(); From f9fc1bb0488ba79c63f82378f3b1f7bc964d56ba Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 22:04:52 -0700 Subject: [PATCH 26/90] Limit admin bar links for unauthorized users --- php/class-customize-snapshot-manager.php | 22 +++++++++++++--------- php/class-post-type.php | 9 +++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 45c7d50e..607602b7 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -642,7 +642,7 @@ public function add_resume_snapshot_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_post_edit_screen_link( $wp_admin_bar ) { - if ( ! $this->snapshot ) { + if ( ! $this->snapshot || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->edit_posts ) ) { return; } $post = $this->snapshot->post(); @@ -665,7 +665,7 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_publish_changeset_link( $wp_admin_bar ) { - if ( ! $this->snapshot ) { + if ( ! $this->snapshot || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) ) { return; } $post = $this->snapshot->post(); @@ -675,7 +675,7 @@ public function add_publish_changeset_link( $wp_admin_bar ) { $href = add_query_arg( array( - 'post_type' => 'customize_changeset', + 'post_type' => $this->post_type->get_slug(), 'action' => 'frontend_publish', 'uuid' => $this->current_snapshot_uuid, 'stylesheet' => get_stylesheet(), @@ -717,7 +717,7 @@ public function add_snapshot_exit_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar Admin bar. */ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { - if ( empty( $this->snapshot ) ) { + if ( empty( $this->snapshot ) || ! current_user_can( 'customize' ) ) { return; } $snapshot_admin_bar_node_ids = array( @@ -1071,16 +1071,18 @@ public function handle_frontend_changset_publish() { } $this->current_snapshot_uuid = $uuid; - if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) { - wp_die( 'insufficient_post_permissions', 403 ); - } - - if ( ! check_ajax_referer( 'publish-changeset_' . $this->current_snapshot_uuid, false, false ) ) { + $is_user_authorized = ( + check_ajax_referer( 'publish-changeset_' . $this->current_snapshot_uuid, false, false ) + && + current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) + ); + if ( ! $is_user_authorized ) { wp_die( esc_html__( 'Oops. Unable to publish the changeset due to an expired user session. Please go back, reload the page, and try publishing again.', 'customize-snapshots' ), esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), array( 'back_link' => true, + 'response' => 401, ) ); } @@ -1095,6 +1097,7 @@ public function handle_frontend_changset_publish() { esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), array( 'back_link' => true, + 'response' => 400, ) ); } @@ -1114,6 +1117,7 @@ public function handle_frontend_changset_publish() { esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), array( 'back_link' => true, + 'response' => 500, ) ); } else { diff --git a/php/class-post-type.php b/php/class-post-type.php index 7398fd1b..6c0ac89f 100644 --- a/php/class-post-type.php +++ b/php/class-post-type.php @@ -65,6 +65,15 @@ public function __construct( Customize_Snapshot_Manager $snapshot_manager ) { $this->snapshot_manager = $snapshot_manager; } + /** + * Get the post type slug. + * + * @return string Post type slug. + */ + public function get_slug() { + return static::SLUG; + } + /** * Calls common hooks Actions and filters */ From 637593804951e0599a5b86dcab29f2dd7a022f1e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 5 Aug 2017 22:08:44 -0700 Subject: [PATCH 27/90] Remove hiding of Add New links for changesets and just redirect post-new.php to Customizer --- php/class-post-type.php | 23 ++++++----------------- tests/php/test-class-post-type.php | 19 ------------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/php/class-post-type.php b/php/class-post-type.php index d695d0ef..d608bb76 100644 --- a/php/class-post-type.php +++ b/php/class-post-type.php @@ -111,7 +111,12 @@ public function init() { add_filter( 'map_meta_cap', array( $this, 'remap_customize_meta_cap' ), 5, 4 ); add_filter( 'bulk_actions-edit-' . static::SLUG, array( $this, 'add_snapshot_bulk_actions' ) ); add_filter( 'handle_bulk_actions-edit-' . static::SLUG, array( $this, 'handle_snapshot_merge' ), 10, 3 ); - add_action( 'admin_print_styles-edit.php', array( $this, 'hide_add_new_changeset_button' ) ); + add_action( 'load-post-new.php', function() { + if ( static::SLUG === get_current_screen()->post_type ) { + wp_redirect( wp_customize_url() ); + exit; + } + } ); } /** @@ -763,22 +768,6 @@ public function hide_disabled_publishing_actions( $post ) { - - assertEquals( 5, has_filter( 'map_meta_cap', array( $post_type_obj, 'remap_customize_meta_cap' ) ) ); $this->assertEquals( 10, has_filter( 'bulk_actions-edit-' . Post_Type::SLUG, array( $post_type_obj, 'add_snapshot_bulk_actions' ) ) ); $this->assertEquals( 10, has_filter( 'handle_bulk_actions-edit-' . Post_Type::SLUG, array( $post_type_obj, 'handle_snapshot_merge' ) ) ); - $this->assertEquals( 10, has_action( 'admin_print_styles-edit.php', array( $post_type_obj, 'hide_add_new_changeset_button' ) ) ); } /** @@ -958,22 +957,4 @@ public function test_remap_customize_meta_cap() { $this->mark_incompatible(); $this->markTestIncomplete(); } - - /** - * Test hide_add_new_changeset_button - * - * @covers \CustomizeSnapshots\Post_Type::hide_add_new_changeset_button() - */ - public function test_hide_add_new_changeset_button() { - $this->mark_incompatible(); - $post_type_obj = new Post_Type( $this->plugin->customize_snapshot_manager ); - global $typenow; - $typenow = Post_Type::SLUG; // WPCS: Global override ok. - ob_start(); - $post_type_obj->hide_add_new_changeset_button(); - $content = ob_get_clean(); - $this->assertContains( 'a.page-title-action', $content ); - $this->assertContains( 'display: none;', $content ); - } - } From 83168f14af5558907e18a2e2d1dd9e9c627c7da8 Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 22 Aug 2017 18:07:55 -0700 Subject: [PATCH 28/90] Update dev-lib --- dev-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-lib b/dev-lib index 8473c072..0408722f 160000 --- a/dev-lib +++ b/dev-lib @@ -1 +1 @@ -Subproject commit 8473c072ddde7d478aa5316f884c4a29ef0372b0 +Subproject commit 0408722f07d1c4bee5f04d541e84ac6d18d9a6c1 From 7b2ad4c42e4b0d19982eb40c1228aef02608c3b9 Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 22 Aug 2017 18:28:05 -0700 Subject: [PATCH 29/90] Update devDependencies --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d2765bc7..b4d8753d 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,14 @@ "author": "XWP", "license": "GPL-2.0+", "devDependencies": { - "eslint": "^3.2.2", - "grunt": "~0.4.5", - "grunt-contrib-clean": "~1.0.0", + "eslint": "^4.5.0", + "grunt": "~1.0.1", + "grunt-contrib-clean": "~1.1.0", "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-cssmin": "~1.0.1", - "grunt-contrib-jshint": "~1.0.0", - "grunt-contrib-uglify": "~1.0.1", - "grunt-shell": "~1.3.0", - "grunt-wp-deploy": "^1.1.0" + "grunt-contrib-cssmin": "~2.2.1", + "grunt-contrib-jshint": "~1.1.0", + "grunt-contrib-uglify": "~3.0.1", + "grunt-shell": "~2.1.0", + "grunt-wp-deploy": "~1.2.1" } } From 3109652de3a7f3e7a845244b721f84750c0d3535 Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 22 Aug 2017 18:36:58 -0700 Subject: [PATCH 30/90] Update dev-lib --- dev-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-lib b/dev-lib index 0408722f..d8540c1f 160000 --- a/dev-lib +++ b/dev-lib @@ -1 +1 @@ -Subproject commit 0408722f07d1c4bee5f04d541e84ac6d18d9a6c1 +Subproject commit d8540c1f036c583f48af5f63c3f6d04db36c0db3 From 9083674d495c59653cea1820017da11b50570c61 Mon Sep 17 00:00:00 2001 From: Derek Herman Date: Tue, 22 Aug 2017 18:37:34 -0700 Subject: [PATCH 31/90] Regenerate readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index f9cb2bb2..231a2be8 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts **Stable tag:** 0.6.2 **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) +[![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?type=dev) ## Description ## From a65174eb8f07c6edcb6f15cd3eef73faf42bc099 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 28 Aug 2017 19:30:02 -0700 Subject: [PATCH 32/90] Add required PHP version go readme --- readme.md | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 231a2be8..55ccb2cf 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,7 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts **Tested up to:** 4.8.1 **Stable tag:** 0.6.2 **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) +**Requires PHP:** 5.3 [![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?type=dev) diff --git a/readme.txt b/readme.txt index 88b6d52e..121e9e64 100644 --- a/readme.txt +++ b/readme.txt @@ -3,6 +3,7 @@ Contributors: xwp, westonruter, valendesigns, utkarshpatel, sayedwp, newscorpau Requires at least: 4.6 Tested up to: 4.8.1 Stable tag: 0.6.2 +Requires PHP: 5.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: customizer, customize, changesets From bd5f2b64a105fd3d513e5c70ea14d788281c6913 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 20 Oct 2017 23:48:20 -0700 Subject: [PATCH 33/90] Remove compat code for WP older than 4.7 Also fix add_snapshot_uuid_to_return_url logic to only include customize_changeset_uuid in return_url when referer had it to begin with. --- js/compat/customize-snapshots-frontend.js | 335 ------ js/compat/customize-snapshots-preview.js | 201 ---- js/compat/customize-snapshots.js | 536 --------- js/customize-migrate.js | 79 -- php/class-customize-snapshot-back-compat.php | 325 ------ php/class-customize-snapshot-command.php | 61 - ...customize-snapshot-manager-back-compat.php | 1030 ----------------- php/class-customize-snapshot-manager.php | 200 ++-- php/class-customize-snapshot.php | 83 -- php/class-migrate.php | 244 ---- php/class-plugin.php | 67 +- php/class-post-type-back-compat.php | 241 ---- php/class-post-type.php | 10 +- readme.txt | 2 +- 14 files changed, 92 insertions(+), 3322 deletions(-) delete mode 100644 js/compat/customize-snapshots-frontend.js delete mode 100644 js/compat/customize-snapshots-preview.js delete mode 100644 js/compat/customize-snapshots.js delete mode 100644 js/customize-migrate.js delete mode 100644 php/class-customize-snapshot-back-compat.php delete mode 100644 php/class-customize-snapshot-command.php delete mode 100644 php/class-customize-snapshot-manager-back-compat.php delete mode 100644 php/class-customize-snapshot.php delete mode 100644 php/class-migrate.php delete mode 100644 php/class-post-type-back-compat.php diff --git a/js/compat/customize-snapshots-frontend.js b/js/compat/customize-snapshots-frontend.js deleted file mode 100644 index aa3abbaa..00000000 --- a/js/compat/customize-snapshots-frontend.js +++ /dev/null @@ -1,335 +0,0 @@ -/* global jQuery, confirm */ -/* exported CustomizeSnapshotsFrontend */ -/* eslint consistent-this: [ "error", "section" ], no-magic-numbers: [ "error", { "ignore": [-1,0,1] } ] */ -/* eslint-disable no-alert */ - -/* - * The code here is derived from the initial Transactions pull request: https://github.com/xwp/wordpress-develop/pull/61 - * See https://github.com/xwp/wordpress-develop/blob/97fd5019c488a0713d34b517bdbff67c62c48a5d/src/wp-includes/js/customize-preview.js#L98-L111 - */ - -var CustomizeSnapshotsFrontend = ( function( $ ) { - 'use strict'; - - var component = { - data: { - uuid: '', - home_url: { - scheme: '', - host: '', - path: '' - }, - l10n: { - restoreSessionPrompt: '' - } - } - }; - - /** - * Init. - * - * @param {object} args Args. - * @param {string} args.uuid UUID. - * @returns {void} - */ - component.init = function init( args ) { - _.extend( component.data, args ); - - component.hasSessionStorage = 'undefined' !== typeof sessionStorage; - - component.keepSessionAlive(); - component.rememberSessionSnapshot(); - component.injectSnapshotIntoLinks(); - component.handleExitSnapshotSessionLink(); - component.injectSnapshotIntoAjaxRequests(); - component.injectSnapshotIntoForms(); - }; - - /** - * Prompt to restore session. - * - * @returns {void} - */ - component.keepSessionAlive = function keepSessionAlive() { - var currentSnapshotUuid, urlParser, adminBarItem; - if ( ! component.hasSessionStorage ) { - return; - } - currentSnapshotUuid = sessionStorage.getItem( 'customize_snapshot_uuid' ); - if ( ! currentSnapshotUuid || component.data.uuid ) { - return; - } - - urlParser = document.createElement( 'a' ); - urlParser.href = location.href; - if ( urlParser.search.length > 1 ) { - urlParser.search += '&'; - } - urlParser.search += 'customize_snapshot_uuid=' + encodeURIComponent( sessionStorage.getItem( 'customize_snapshot_uuid' ) ); - - $( function() { - adminBarItem = $( '#wp-admin-bar-resume-customize-snapshot' ); - if ( adminBarItem.length ) { - adminBarItem.find( '> a' ).prop( 'href', urlParser.href ); - adminBarItem.show(); - } else if ( confirm( component.data.l10n.restoreSessionPrompt ) ) { - location.replace( urlParser.href ); - } else { - sessionStorage.removeItem( 'customize_snapshot_uuid' ); - } - } ); - }; - - /** - * Remember the session's snapshot. - * - * Persist the snapshot UUID in session storage so that we can prompt to restore the snapshot query param if inadvertently dropped. - * - * @returns {void} - */ - component.rememberSessionSnapshot = function rememberSessionSnapshot() { - if ( ! component.hasSessionStorage || ! component.data.uuid ) { - return; - } - sessionStorage.setItem( 'customize_snapshot_uuid', component.data.uuid ); - }; - - /** - * Inject the snapshot UUID into links in the document. - * - * @returns {void} - */ - component.injectSnapshotIntoLinks = function injectSnapshotIntoLinks() { - var linkSelectors = 'a, area'; - - if ( ! component.data.uuid ) { - return; - } - $( function() { - - // Inject links into initial document. - $( document.body ).find( linkSelectors ).each( function() { - component.injectSnapshotLinkParam( this ); - } ); - - // Inject links for new elements added to the page - if ( 'undefined' !== typeof MutationObserver ) { - component.mutationObserver = new MutationObserver( function( mutations ) { - _.each( mutations, function( mutation ) { - $( mutation.target ).find( linkSelectors ).each( function() { - component.injectSnapshotLinkParam( this ); - } ); - } ); - } ); - component.mutationObserver.observe( document.documentElement, { - childList: true, - subtree: true - } ); - } else { - - // If mutation observers aren't available, fallback to just-in-time injection. - $( document.documentElement ).on( 'click focus mouseover', linkSelectors, function() { - component.injectSnapshotLinkParam( this ); - } ); - } - } ); - }; - - /** - * Is matching base URL (host and path)? - * - * @param {HTMLAnchorElement} parsedUrl Parsed URL. - * @param {string} parsedUrl.hostname Host. - * @param {string} parsedUrl.pathname Path. - * @returns {boolean} Whether matched. - */ - component.isMatchingBaseUrl = function isMatchingBaseUrl( parsedUrl ) { - return parsedUrl.hostname === component.data.home_url.host && 0 === parsedUrl.pathname.indexOf( component.data.home_url.path ); - }; - - /** - * Should the supplied link have a snapshot UUID added (or does it have one already)? - * - * @param {HTMLAnchorElement|HTMLAreaElement} element Link element. - * @param {string} element.search Query string. - * @param {string} element.pathname Path. - * @param {string} element.hostname Hostname. - * @returns {boolean} Is appropriate for snapshot link. - */ - component.shouldLinkHaveSnapshotParam = function shouldLinkHaveSnapshotParam( element ) { - if ( ! component.isMatchingBaseUrl( element ) ) { - return false; - } - - // Skip wp login and signup pages. - if ( /\/wp-(login|signup)\.php$/.test( element.pathname ) ) { - return false; - } - - // Allow links to admin ajax as faux frontend URLs. - if ( /\/wp-admin\/admin-ajax\.php$/.test( element.pathname ) ) { - return true; - } - - // Disallow links to admin. - if ( /\/wp-admin(\/|$)/.test( element.pathname ) ) { - return false; - } - - // Skip links in admin bar. - if ( $( element ).closest( '#wpadminbar' ).length ) { - return false; - } - - return true; - }; - - /** - * Return whether the supplied link element has the snapshot query param. - * - * @param {HTMLAnchorElement|HTMLAreaElement} element Link element. - * @param {object} element.search Query string. - * @returns {boolean} Whether query param is present. - */ - component.doesLinkHaveSnapshotQueryParam = function( element ) { - return /(^|&)customize_snapshot_uuid=/.test( element.search.substr( 1 ) ); - }; - - /** - * Inject the customize_snapshot_uuid query param into links on the frontend. - * - * @param {HTMLAnchorElement|HTMLAreaElement} element Link element. - * @param {object} element.search Query string. - * @returns {void} - */ - component.injectSnapshotLinkParam = function injectSnapshotLinkParam( element ) { - if ( component.doesLinkHaveSnapshotQueryParam( element ) || ! component.shouldLinkHaveSnapshotParam( element ) ) { - return; - } - - if ( element.search.length > 1 ) { - element.search += '&'; - } - element.search += 'customize_snapshot_uuid=' + encodeURIComponent( component.data.uuid ); - }; - - /** - * Handle electing to exit from the snapshot session. - * - * @returns {void} - */ - component.handleExitSnapshotSessionLink = function handleExitSnapshotSessionLink() { - $( function() { - if ( ! component.hasSessionStorage ) { - return; - } - $( '#wpadminbar' ).on( 'click', '#wp-admin-bar-exit-customize-snapshot', function() { - sessionStorage.removeItem( 'customize_snapshot_uuid' ); - } ); - } ); - }; - - /** - * Inject the snapshot UUID into Ajax requests. - * - * @return {void} - */ - component.injectSnapshotIntoAjaxRequests = function injectSnapshotIntoAjaxRequests() { - $.ajaxPrefilter( component.prefilterAjax ); - }; - - /** - * Rewrite Ajax requests to inject Customizer state. - * - * @param {object} options Options. - * @param {string} options.type Type. - * @param {string} options.url URL. - * @returns {void} - */ - component.prefilterAjax = function prefilterAjax( options ) { - var urlParser; - if ( ! component.data.uuid ) { - return; - } - - urlParser = document.createElement( 'a' ); - urlParser.href = options.url; - - // Abort if the request is not for this site. - if ( ! component.isMatchingBaseUrl( urlParser ) ) { - return; - } - - // Skip if snapshot UUID already in URL. - if ( -1 !== urlParser.search.indexOf( 'customize_snapshot_uuid=' + component.data.uuid ) ) { - return; - } - - if ( urlParser.search.substr( 1 ).length > 0 ) { - urlParser.search += '&'; - } - urlParser.search += 'customize_snapshot_uuid=' + component.data.uuid; - - options.url = urlParser.href; - }; - - /** - * Inject snapshot into forms, allowing preview to persist through submissions. - * - * @returns {void} - */ - component.injectSnapshotIntoForms = function injectSnapshotIntoForms() { - if ( ! component.data.uuid ) { - return; - } - $( function() { - - // Inject inputs for forms in initial document. - $( document.body ).find( 'form' ).each( function() { - component.injectSnapshotFormInput( this ); - } ); - - // Inject inputs for new forms added to the page. - if ( 'undefined' !== typeof MutationObserver ) { - component.mutationObserver = new MutationObserver( function( mutations ) { - _.each( mutations, function( mutation ) { - $( mutation.target ).find( 'form' ).each( function() { - component.injectSnapshotFormInput( this ); - } ); - } ); - } ); - component.mutationObserver.observe( document.documentElement, { - childList: true, - subtree: true - } ); - } - } ); - }; - - /** - * Inject snapshot into form inputs. - * - * @param {HTMLFormElement} form Form. - * @returns {void} - */ - component.injectSnapshotFormInput = function injectSnapshotFormInput( form ) { - var urlParser; - if ( $( form ).find( 'input[name=customize_snapshot_uuid]' ).length > 0 ) { - return; - } - urlParser = document.createElement( 'a' ); - urlParser.href = form.action; - if ( ! component.isMatchingBaseUrl( urlParser ) ) { - return; - } - - $( form ).prepend( $( '', { - type: 'hidden', - name: 'customize_snapshot_uuid', - value: component.data.uuid - } ) ); - }; - - return component; -} )( jQuery ); - diff --git a/js/compat/customize-snapshots-preview.js b/js/compat/customize-snapshots-preview.js deleted file mode 100644 index 78aa8fbc..00000000 --- a/js/compat/customize-snapshots-preview.js +++ /dev/null @@ -1,201 +0,0 @@ -/* global jQuery, JSON */ -/* exported CustomizeSnapshotsPreview */ -/* eslint consistent-this: [ "error", "section" ], no-magic-numbers: [ "error", { "ignore": [-1,0,1] } ] */ - -/* - * The code here is derived from Customize REST Resources: https://github.com/xwp/wp-customize-rest-resources - */ - -var CustomizeSnapshotsPreview = (function( api, $ ) { - 'use strict'; - - var component = { - data: { - home_url: { - scheme: '', - host: '', - path: '' - }, - rest_api_url: { - scheme: '', - host: '', - path: '' - }, - admin_ajax_url: { - scheme: '', - host: '', - path: '' - }, - initial_dirty_settings: [] - } - }; - - /** - * Init. - * - * @param {object} args Args. - * @param {string} args.uuid UUID. - * @returns {void} - */ - component.init = function init( args ) { - _.extend( component.data, args ); - - component.injectSnapshotIntoAjaxRequests(); - component.handleFormSubmissions(); - }; - - /** - * Get customize query vars. - * - * @see wp.customize.previewer.query - * - * @returns {{ - * customized: string, - * nonce: string, - * wp_customize: string, - * theme: string - * }} Query vars. - */ - component.getCustomizeQueryVars = function getCustomizeQueryVars() { - var customized = {}; - api.each( function( setting ) { - if ( setting._dirty || -1 !== _.indexOf( component.data.initial_dirty_settings, setting.id ) ) { - customized[ setting.id ] = setting.get(); - } - } ); - return { - wp_customize: 'on', - theme: api.settings.theme.stylesheet, - nonce: api.settings.nonce.preview, - customized: JSON.stringify( customized ) - }; - }; - - /** - * Inject the snapshot UUID into Ajax requests. - * - * @return {void} - */ - component.injectSnapshotIntoAjaxRequests = function injectSnapshotIntoAjaxRequests() { - $.ajaxPrefilter( component.prefilterAjax ); - }; - - /** - * Rewrite Ajax requests to inject Customizer state. - * - * This will not work 100% of the time, such as if an Admin Ajax handler is - * specifically looking for a $_GET param vs a $_POST param. - * - * @param {object} options Options. - * @param {string} options.type Type. - * @param {string} options.url URL. - * @param {object} originalOptions Original options. - * @param {XMLHttpRequest} xhr XHR. - * @returns {void} - */ - component.prefilterAjax = function prefilterAjax( options, originalOptions, xhr ) { - var requestMethod, urlParser, queryVars, isMatchingHomeUrl, isMatchingRestUrl, isMatchingAdminAjaxUrl; - - urlParser = document.createElement( 'a' ); - urlParser.href = options.url; - - isMatchingHomeUrl = urlParser.host === component.data.home_url.host && 0 === urlParser.pathname.indexOf( component.data.home_url.path ); - isMatchingRestUrl = urlParser.host === component.data.rest_api_url.host && 0 === urlParser.pathname.indexOf( component.data.rest_api_url.path ); - isMatchingAdminAjaxUrl = urlParser.host === component.data.admin_ajax_url.host && 0 === urlParser.pathname.indexOf( component.data.admin_ajax_url.path ); - - if ( ! isMatchingHomeUrl && ! isMatchingRestUrl && ! isMatchingAdminAjaxUrl ) { - return; - } - - requestMethod = options.type.toUpperCase(); - - // Customizer currently requires POST requests, so use override (force Backbone.emulateHTTP). - if ( 'POST' !== requestMethod ) { - xhr.setRequestHeader( 'X-HTTP-Method-Override', requestMethod ); - options.type = 'POST'; - } - - if ( options.data && 'GET' === requestMethod ) { - /* - * Make sure the query vars for the REST API persist in GET (since - * REST API explicitly look at $_GET['filter']). - * We have to make sure the REST query vars are added as GET params - * when the method is GET as otherwise they won't be parsed properly. - * The issue lies in \WP_REST_Request::get_parameter_order() which - * only is looking at \WP_REST_Request::$method instead of $_SERVER['REQUEST_METHOD']. - * @todo Improve \WP_REST_Request::get_parameter_order() to be more aware of X-HTTP-Method-Override - */ - if ( urlParser.search.substr( 1 ).length > 1 ) { - urlParser.search += '&'; - } - urlParser.search += options.data; - } - - // Add Customizer post data. - if ( options.data ) { - options.data += '&'; - } else { - options.data = ''; - } - queryVars = component.getCustomizeQueryVars(); - queryVars.wp_customize_preview_ajax = 'true'; - options.data += $.param( queryVars ); - }; - - /** - * Handle form submissions. - * - * This fixes Core ticket {@link https://core.trac.wordpress.org/ticket/20714|#20714: Theme customizer: Impossible to preview a search results page} - * Implements todo in {@link https://github.com/xwp/wordpress-develop/blob/4.5.3/src/wp-includes/js/customize-preview.js#L69-L73} - * - * @returns {void} - */ - component.handleFormSubmissions = function handleFormSubmissions() { - $( function() { - - // Defer so that we can be sure that our event handler will come after any other event handlers. - _.defer( function() { - component.replaceFormSubmitHandler(); - } ); - } ); - }; - - /** - * Replace form submit handler. - * - * @returns {void} - */ - component.replaceFormSubmitHandler = function replaceFormSubmitHandler() { - var body = $( document.body ); - body.off( 'submit.preview' ); - body.on( 'submit.preview', 'form', function( event ) { - var urlParser; - - /* - * If the default wasn't prevented already (in which case the form - * submission is already being handled by JS), and if it has a GET - * request method, then take the serialized form data and add it as - * a query string to the action URL and send this in a url message - * to the Customizer pane so that it will be loaded. If the form's - * action points to a non-previewable URL, the the Customizer pane's - * previewUrl setter will reject it so that the form submission is - * a no-op, which is the same behavior as when clicking a link to an - * external site in the preview. - */ - if ( ! event.isDefaultPrevented() && 'GET' === this.method.toUpperCase() ) { - urlParser = document.createElement( 'a' ); - urlParser.href = this.action; - if ( urlParser.search.substr( 1 ).length > 1 ) { - urlParser.search += '&'; - } - urlParser.search += $( this ).serialize(); - api.preview.send( 'url', urlParser.href ); - } - - // Now preventDefault as is done on the normal submit.preview handler in customize-preview.js. - event.preventDefault(); - } ); - }; - - return component; -} )( wp.customize, jQuery ); diff --git a/js/compat/customize-snapshots.js b/js/compat/customize-snapshots.js deleted file mode 100644 index 311d0938..00000000 --- a/js/compat/customize-snapshots.js +++ /dev/null @@ -1,536 +0,0 @@ -/* global jQuery, wp, _customizeSnapshotsCompatSettings, JSON */ -/* eslint consistent-this: ["error", "snapshot"], no-magic-numbers: [ "error", { "ignore": [0,1,2] } ] */ - -( function( api, $ ) { - 'use strict'; - - api.SnapshotsCompat = api.Snapshots.extend( { - - uuidParam: 'customize_snapshot_uuid', - - initialize: function initialize( snapshotsConfig ) { - var snapshot = this; - - if ( _.isObject( snapshotsConfig ) ) { - _.extend( snapshot.data, snapshotsConfig ); - } - - window._wpCustomizeControlsL10n.save = snapshot.data.i18n.publish; - window._wpCustomizeControlsL10n.saved = snapshot.data.i18n.published; - - api.bind( 'ready', function() { - api.state.create( 'snapshot-exists', snapshot.data.snapshotExists ); - - if ( api.state( 'snapshot-exists' ).get() ) { - api.state( 'saved' ).set( false ); - snapshot.resetSavedStateQuietly(); - } - } ); - - // Make sure that saved state is false so that Published button behaves as expected. - api.bind( 'save', function() { - api.state( 'saved' ).set( false ); - } ); - - api.bind( 'change', function() { - api.state( 'snapshot-saved' ).set( false ); - } ); - - api.bind( 'saved', function( response ) { - var url = window.location.href, - updatedUrl, - urlParts; - - // Update the UUID. - if ( response.new_customize_snapshot_uuid ) { - snapshot.data.uuid = response.new_customize_snapshot_uuid; - snapshot.previewLink.attr( 'target', snapshot.data.uuid ); - } - - snapshot.removeParamFromClose( 'customize_snapshot_uuid' ); - - api.state( 'snapshot-exists' ).set( false ); - - // Replace the history state with an updated Customizer URL that does not include the Snapshot UUID. - urlParts = url.split( '?' ); - if ( history.replaceState && urlParts[1] ) { - updatedUrl = urlParts[0] + '?' + _.filter( urlParts[1].split( '&' ), function( queryPair ) { - return ! /^(customize_snapshot_uuid)=/.test( queryPair ); - } ).join( '&' ); - updatedUrl = updatedUrl.replace( /\?$/, '' ); - if ( updatedUrl !== url ) { - history.replaceState( {}, document.title, updatedUrl ); - } - } - } ); - - api.Snapshots.prototype.initialize.call( snapshot, snapshotsConfig ); - }, - - /** - * Update button text. - * - * @returns {void} - */ - updateButtonText: function updateButtonText() { - var snapshot = this, date = snapshot.getDateFromInputs(); - if ( snapshot.isFutureDate() && date && snapshot.data.currentUserCanPublish ) { - snapshot.snapshotButton.text( snapshot.data.i18n.scheduleButton ); - } else { - snapshot.snapshotButton.text( api.state( 'snapshot-exists' ).get() ? snapshot.data.i18n.updateButton : snapshot.data.i18n.saveButton ); - } - }, - - /** - * Make the AJAX request to update/save a snapshot. - * - * @param {object} options Options. - * @param {string} options.status The post status for the snapshot. - * @return {void} - */ - sendUpdateSnapshotRequest: function sendUpdateSnapshotRequest( options ) { - var snapshot = this, - spinner = $( '#customize-header-actions' ).find( '.spinner' ), - request, data; - - data = _.extend( - { - status: 'draft' - }, - api.previewer.query(), - options, - { - nonce: api.settings.nonce.snapshot, - customize_snapshot_uuid: snapshot.data.uuid - } - ); - - request = wp.ajax.post( 'customize_update_snapshot', data ); - - spinner.addClass( 'is-active' ); - request.always( function( response ) { - spinner.removeClass( 'is-active' ); - if ( response.edit_link ) { - snapshot.data.editLink = response.edit_link; - } - if ( response.snapshot_publish_date ) { - snapshot.data.publishDate = response.snapshot_publish_date; - } - if ( response.title ) { - snapshot.data.title = response.title; - } - snapshot.updateSnapshotEditControls(); - snapshot.data.dirty = false; - - // @todo Remove privateness from _handleSettingValidities in Core. - if ( api._handleSettingValidities && response.setting_validities ) { - api._handleSettingValidities( { - settingValidities: response.setting_validities, - focusInvalidControl: true - } ); - } - } ); - - request.done( function( response ) { - var url = api.previewer.previewUrl(), - regex = new RegExp( '([?&])customize_snapshot_uuid=.*?(&|$)', 'i' ), - notFound = -1, - separator = url.indexOf( '?' ) !== notFound ? '&' : '?', - customizeUrl = window.location.href, - customizeSeparator = customizeUrl.indexOf( '?' ) !== notFound ? '&' : '?'; - - if ( url.match( regex ) ) { - url = url.replace( regex, '$1customize_snapshot_uuid=' + encodeURIComponent( snapshot.data.uuid ) + '$2' ); - } else { - url = url + separator + 'customize_snapshot_uuid=' + encodeURIComponent( snapshot.data.uuid ); - } - - // Change the save button text to update. - api.state( 'snapshot-exists' ).set( true ); - - // Replace the history state with an updated Customizer URL that includes the Snapshot UUID. - if ( history.replaceState && ! customizeUrl.match( regex ) ) { - customizeUrl += customizeSeparator + 'customize_snapshot_uuid=' + encodeURIComponent( snapshot.data.uuid ); - history.replaceState( {}, document.title, customizeUrl ); - } - - api.state( 'snapshot-saved' ).set( true ); - if ( 'pending' === data.status ) { - api.state( 'snapshot-submitted' ).set( true ); - } - snapshot.resetSavedStateQuietly(); - - // Trigger an event for plugins to use. - api.trigger( 'customize-snapshots-update', { - previewUrl: url, - customizeUrl: customizeUrl, - uuid: snapshot.data.uuid, - response: response - } ); - } ); - - request.fail( function( response ) { - var id = 'snapshot-dialog-error', - snapshotDialogShareError = wp.template( id ), - messages = snapshot.data.i18n.errorMsg, - invalidityCount = 0, - dialogElement; - - if ( response.setting_validities ) { - invalidityCount = _.size( response.setting_validities, function( validity ) { - return true !== validity; - } ); - } - - /* - * Short-circuit if there are setting validation errors, since the error messages - * will be displayed with the controls themselves. Eventually, once we have - * a global notification area in the Customizer, we can eliminate this - * short-circuit and instead display the messages in there. - * See https://core.trac.wordpress.org/ticket/35210 - */ - if ( invalidityCount > 0 ) { - return; - } - - if ( response.errors ) { - messages += ' ' + _.pluck( response.errors, 'message' ).join( ' ' ); - } - - // Insert the snapshot dialog error template. - dialogElement = $( '#' + id ); - if ( ! dialogElement.length ) { - dialogElement = $( snapshotDialogShareError( { - title: snapshot.data.i18n.errorTitle, - message: messages - } ) ); - $( 'body' ).append( dialogElement ); - } - - // Open the dialog. - dialogElement.dialog( { - autoOpen: true, - modal: true - } ); - } ); - - return request; - }, - - /** - * Amend the preview query so we can update the snapshot during `customize_save`. - * - * @return {void} - */ - extendPreviewerQuery: function extendPreviewerQuery() { - var snapshot = this, originalQuery = api.previewer.query; - - api.previewer.query = function() { - var retval = originalQuery.apply( this, arguments ); - retval.customizer_state_query_vars = JSON.stringify( snapshot.getStateQueryVars() ); - - if ( api.state( 'snapshot-exists' ).get() ) { - retval.customize_snapshot_uuid = snapshot.data.uuid; - if ( snapshot.snapshotTitle && snapshot.snapshotTitle.val() ) { - retval.title = snapshot.snapshotTitle.val(); - } - } - return retval; - }; - }, - - /** - * Create the snapshot buttons. - * - * @return {void} - */ - addButtons: function addButtons() { - var snapshot = this, - header = $( '#customize-header-actions' ), - disableButton = true, - templateData = {}, setPreviewLinkHref, currentTheme, - savedPreviewingTheme, themeNotActiveOrSaved; - - snapshot.publishButton = header.find( '#save' ); - snapshot.spinner = header.find( '.spinner' ); - snapshot.dirtyScheduleDate = new api.Value(); - - // Save/update button. - if ( api.state( 'snapshot-exists' ).get() ) { - if ( 'future' === snapshot.data.postStatus ) { - templateData.buttonText = snapshot.data.i18n.scheduleButton; - } else { - templateData.buttonText = snapshot.data.i18n.updateButton; - } - } else { - templateData.buttonText = snapshot.data.i18n.saveButton; - } - - snapshot.snapshotButton = $( $.trim( wp.template( 'snapshot-save' )( templateData ) ) ); - - if ( ! snapshot.data.currentUserCanPublish ) { - snapshot.snapshotButton.attr( 'title', api.state( 'snapshot-exists' ).get() ? snapshot.data.i18n.permsMsg.update : snapshot.data.i18n.permsMsg.save ); - } - - currentTheme = api.settings.theme.stylesheet; // Or previewing theme. - savedPreviewingTheme = snapshot.data.previewingTheme; - themeNotActiveOrSaved = ! api.state( 'activated' ).get() && ! savedPreviewingTheme; - snapshot.isNotSavedPreviewingTheme = savedPreviewingTheme && savedPreviewingTheme !== currentTheme; - - if ( themeNotActiveOrSaved || snapshot.isNotSavedPreviewingTheme ) { - disableButton = false; - } - - snapshot.snapshotButton.prop( 'disabled', disableButton ); - - snapshot.snapshotButton.on( 'click', function( event ) { - var status; - event.preventDefault(); - status = snapshot.isFutureDate() ? 'future' : 'draft'; - - snapshot.snapshotButton.prop( 'disabled', true ); - snapshot.updateSnapshot( status ).done( function() { - snapshot.snapshotButton.prop( 'disabled', true ); - } ).fail( function() { - snapshot.snapshotButton.prop( 'disabled', false ); - } ); - } ); - - snapshot.snapshotButton.insertAfter( snapshot.publishButton ); - - // Preview link. - snapshot.previewLink = $( $.trim( wp.template( 'snapshot-preview-link' )() ) ); - snapshot.previewLink.toggle( api.state( 'snapshot-saved' ).get() ); - snapshot.previewLink.attr( 'target', snapshot.data.uuid ); - setPreviewLinkHref = _.debounce( function() { - if ( api.state( 'snapshot-exists' ).get() ) { - snapshot.previewLink.attr( 'href', snapshot.getSnapshotFrontendPreviewUrl() ); - } else { - snapshot.previewLink.attr( 'href', snapshot.frontendPreviewUrl.get() ); - } - } ); - snapshot.frontendPreviewUrl.bind( setPreviewLinkHref ); - setPreviewLinkHref(); - api.state.bind( 'change', setPreviewLinkHref ); - api.bind( 'saved', setPreviewLinkHref ); - snapshot.snapshotButton.after( snapshot.previewLink ); - api.state( 'snapshot-saved' ).bind( function( saved ) { - snapshot.previewLink.toggle( saved ); - } ); - - // Edit button. - snapshot.snapshotExpandButton = $( $.trim( wp.template( 'snapshot-expand-button' )( {} ) ) ); - snapshot.snapshotExpandButton.insertAfter( snapshot.snapshotButton ); - - if ( ! snapshot.data.editLink ) { - snapshot.snapshotExpandButton.hide(); - } - - api.state( 'change', function() { - snapshot.snapshotExpandButton.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() ); - } ); - - api.state( 'snapshot-exists' ).bind( function( exist ) { - snapshot.snapshotExpandButton.toggle( exist ); - } ); - - api.state( 'snapshot-saved' ).bind( function( saved ) { - snapshot.snapshotButton.prop( 'disabled', saved ); - } ); - - api.state( 'saved' ).bind( function( saved ) { - if ( saved ) { - snapshot.snapshotButton.prop( 'disabled', true ); - } - } ); - api.bind( 'change', function() { - snapshot.snapshotButton.prop( 'disabled', false ); - } ); - - api.state( 'snapshot-exists' ).bind( function( exists ) { - var buttonText, permsMsg; - if ( exists ) { - buttonText = snapshot.data.i18n.updateButton; - permsMsg = snapshot.data.i18n.permsMsg.update; - } else { - buttonText = snapshot.data.i18n.saveButton; - permsMsg = snapshot.data.i18n.permsMsg.save; - } - - snapshot.snapshotButton.text( buttonText ); - if ( ! snapshot.data.currentUserCanPublish ) { - snapshot.snapshotButton.attr( 'title', permsMsg ); - } - } ); - - snapshot.editControlSettings.bind( 'change', function() { - snapshot.snapshotButton.prop( 'disabled', false ); - snapshot.updateButtonText(); - } ); - snapshot.dirtyScheduleDate.bind( function( dirty ) { - var date; - if ( dirty ) { - date = snapshot.getDateFromInputs(); - if ( ! date || ! snapshot.data.currentUserCanPublish ) { - return; - } - snapshot.snapshotButton.text( snapshot.data.i18n.scheduleButton ); - } else { - snapshot.updateButtonText(); - } - } ); - - // Submit for review button. - if ( ! snapshot.data.currentUserCanPublish ) { - snapshot.addSubmitButton(); - } - - header.addClass( 'button-added' ); - }, - - /** - * Silently update the saved state to be true without triggering the - * changed event so that the AYS beforeunload dialog won't appear - * if no settings have been changed after saving a snapshot. Note - * that it would be better if jQuery's callbacks allowed them to - * disabled and then re-enabled later, for example: - * wp.customize.state.topics.change.disable(); - * wp.customize.state( 'saved' ).set( true ); - * wp.customize.state.topics.change.enable(); - * But unfortunately there is no such enable method. - * - * @return {void} - */ - resetSavedStateQuietly: function resetSavedStateQuietly() { - api.state( 'saved' )._value = true; - }, - - /** - * Overrides the autoSaveEditBox method used in api.Snapshots - * because we do not auto save in < 4.7. - * - * @inheritdoc - */ - autoSaveEditBox: function autoSaveEditor() { - - }, - - /** - * Renders snapshot schedule and handles it's events. - * - * @returns {void} - */ - editSnapshotUI: function editSnapshotUI() { - var snapshot = this; - api.Snapshots.prototype.editSnapshotUI.call( snapshot ); - - api.state( 'saved' ).bind( function( saved ) { - if ( saved && ! _.isEmpty( snapshot.editContainer ) ) { - snapshot.data.dirty = false; - snapshot.data.publishDate = snapshot.getCurrentTime(); - snapshot.snapshotEditContainerDisplayed.set( false ); - snapshot.updateSnapshotEditControls(); - } - } ); - }, - - /** - * Updates snapshot schedule with `snapshot.data`. - * - * @return {void} - */ - updateSnapshotEditControls: function updateSnapshotEditControls() { - var snapshot = this, parsed, - sliceBegin = 0, - sliceEnd = -2; - - if ( _.isEmpty( snapshot.editContainer ) ) { - return; - } - - if ( snapshot.data.currentUserCanPublish ) { - if ( '0000-00-00 00:00:00' === snapshot.data.publishDate ) { - snapshot.data.publishDate = snapshot.getCurrentTime(); - } - - // Normalize date with seconds removed. - snapshot.data.publishDate = snapshot.data.publishDate.slice( sliceBegin, sliceEnd ) + '00'; - parsed = snapshot.parseDateTime( snapshot.data.publishDate ); - - // Update date controls. - snapshot.schedule.inputs.each( function() { - var input = $( this ), - fieldName = input.data( 'date-input' ); - - $( this ).val( parsed[fieldName] ); - } ); - } - - snapshot.editContainer.find( 'a.snapshot-edit-link' ) - .attr( 'href', snapshot.data.editLink ) - .show(); - if ( ! _.isEmpty( snapshot.data.title ) ) { - snapshot.snapshotTitle.val( snapshot.data.title ); - } - snapshot.populateSetting(); - }, - - /** - * Populate setting value from the inputs. - * - * @returns {void} - */ - populateSetting: function populateSetting() { - var snapshot = this, - date = snapshot.getDateFromInputs(), - scheduled, isDirtyDate; - - snapshot.editControlSettings( 'title' ).set( snapshot.snapshotTitle.val() ); - - if ( ! date || ! snapshot.data.currentUserCanPublish ) { - return; - } - - date.setSeconds( 0 ); - scheduled = snapshot.formatDate( date ) !== snapshot.data.publishDate; - - isDirtyDate = scheduled && snapshot.isFutureDate(); - snapshot.dirtyScheduleDate.set( isDirtyDate ); - snapshot.editControlSettings( 'date' ).set( snapshot.formatDate( date ) ); - - snapshot.updateCountdown(); - snapshot.editContainer.find( '.reset-time' ).toggle( scheduled ); - }, - - /** - * Parse query string. - * - * Polyfill for function was introduced into core in 4.7 as wp.customize.utils.parseQueryString. - * - * @param {string} queryString Query string. - * @returns {object} Parsed query string. - */ - parseQueryString: function parseQueryString( queryString ) { - var queryParams = {}; - _.each( queryString.split( '&' ), function( pair ) { - var parts, key, value; - parts = pair.split( '=', 2 ); - if ( ! parts[0] ) { - return; - } - key = decodeURIComponent( parts[0].replace( /\+/g, ' ' ) ); - key = key.replace( / /g, '_' ); // What PHP does. - if ( _.isUndefined( parts[1] ) ) { - value = null; - } else { - value = decodeURIComponent( parts[1].replace( /\+/g, ' ' ) ); - } - queryParams[ key ] = value; - } ); - return queryParams; - } - } ); - - api.snapshotsCompat = new api.SnapshotsCompat( _customizeSnapshotsCompatSettings ); - -} )( wp.customize, jQuery ); diff --git a/js/customize-migrate.js b/js/customize-migrate.js deleted file mode 100644 index d4183b25..00000000 --- a/js/customize-migrate.js +++ /dev/null @@ -1,79 +0,0 @@ -/* global jQuery, wp */ -(function( $ ) { - 'use strict'; - var component = { - doingAjax: false, - postMigrationCount: 5 - }; - - /** - * Initialize js. - * - * @return {void} - */ - component.init = function() { - $( function() { - component.el = $( '#customize-snapshot-migration' ); - component.bindClick(); - component.spinner = $( '.spinner.customize-snapshot-spinner' ); - component.spinner.css( 'margin', '0' ); - } ); - }; - - /** - * Bind migrate click event. - * - * @return {void} - */ - component.bindClick = function() { - component.el.click( function() { - if ( component.doingAjax ) { - return; - } - component.spinner.css( 'visibility', 'visible' ); - component.doingAjax = true; - component.migrate( component.el.data( 'nonce' ), component.postMigrationCount ); - } ); - }; - - /** - * Initiate migrate ajax request. - * - * @param {String} nonce Nonce. - * @param {Number} limit Limit for migrate posts. - * - * @return {void} - */ - component.migrate = function( nonce, limit ) { - var request, - requestData = { - nonce: nonce, - limit: limit - }; - - request = wp.ajax.post( 'customize_snapshot_migration', requestData ); - - request.done( function( data ) { - var outerDiv = $( 'div.customize-snapshot-migration' ), delay = 100, newLimit; - if ( data.remaining_posts ) { - newLimit = data.remaining_posts > limit ? limit : data.remaining_posts; - _.delay( component.migrate, delay, nonce, newLimit ); - } else { - component.spinner.css( 'visibility', 'hidden' ); - outerDiv.removeClass( 'notice-error' ).addClass( 'notice-success' ).find( 'p' ).html( component.el.data( 'migration-success' ) ); - component.doingAjax = false; - } - } ); - - request.fail( function() { - component.spinner.css( 'visibility', 'initial' ); - component.doingAjax = false; - if ( window.console ) { - window.console.error( 'Migration ajax failed. Click notice to start it again.' ); - } - } ); - }; - - component.init(); - -})( jQuery ); diff --git a/php/class-customize-snapshot-back-compat.php b/php/class-customize-snapshot-back-compat.php deleted file mode 100644 index 2afa58d4..00000000 --- a/php/class-customize-snapshot-back-compat.php +++ /dev/null @@ -1,325 +0,0 @@ -snapshot_manager = $snapshot_manager; - $this->data = array(); - - if ( ! Customize_Snapshot_Manager_Back_Compat::is_valid_uuid( $uuid ) ) { - throw new Exception( __( 'You\'ve entered an invalid snapshot UUID.', 'customize-snapshots' ) ); - } - $this->uuid = $uuid; - $post = $this->post(); - if ( $post ) { - $this->data = $this->snapshot_manager->post_type->get_post_content( $post ); - } - parent::__construct( $snapshot_manager ); - } - - /** - * Get the snapshot uuid. - * - * @return string - */ - public function uuid() { - return $this->uuid; - } - - /** - * Get the underlying data for the snapshot. - * - * @return array - */ - public function data() { - return $this->data; - } - - /** - * Get the status of the snapshot. - * - * @return string|null - */ - public function status() { - $post = $this->post(); - return $post ? get_post_status( $post->ID ) : null; - } - - /** - * Get the snapshot post associated with the provided UUID, or null if it does not exist. - * - * @return \WP_Post|null Post or null. - */ - public function post() { - if ( ! $this->post_id ) { - $this->post_id = $this->snapshot_manager->post_type->find_post( $this->uuid ); - } - if ( $this->post_id ) { - return get_post( $this->post_id ); - } else { - return null; - } - } - - /** - * Return the Customizer settings corresponding to the data contained in the snapshot. - * - * @return \WP_Customize_Setting[] - */ - public function settings() { - $settings = array(); - $setting_ids = array_keys( $this->data ); - $this->snapshot_manager->customize_manager->add_dynamic_settings( $setting_ids ); - foreach ( $setting_ids as $setting_id ) { - $setting = $this->snapshot_manager->customize_manager->get_setting( $setting_id ); - if ( $setting ) { - $settings[] = $setting; - } - } - return $settings; - } - - /** - * Prepare snapshot data for saving. - * - * @see WP_Customize_Manager::set_post_value() - * @throws Exception When $settings_data is not an array of arrays. - * - * @param array $settings_data Settings data, mapping setting IDs to arrays containing `value` and optionally additional params. - * @param array $options { - * Additional options. - * - * @type bool $skip_validation Whether to skip validation. Optional, defaults to false. - * } - * @return array { - * Result. - * - * @type null|\WP_Error $error Error object if error. - * @type array $sanitized Sanitized values. - * @type array $validities Setting validities. - * } - */ - public function set( array $settings_data, array $options = array() ) { - $error = new \WP_Error(); - $result = array( - 'errors' => null, - 'sanitized' => array(), - 'validities' => array(), - ); - - $setting_ids = array_keys( $settings_data ); - $customize_manager = $this->snapshot_manager->customize_manager; - $customize_manager->add_dynamic_settings( $setting_ids ); - - // Check for recognized settings and authorized settings. - $unsanitized_values = array(); - $unrecognized_setting_ids = array(); - $unauthorized_setting_ids = array(); - foreach ( $settings_data as $setting_id => $setting_params ) { - if ( ! is_array( $setting_params ) ) { - throw new Exception( '$setting_params not an array' ); - } - $setting = $customize_manager->get_setting( $setting_id ); - if ( ! $setting ) { - $unrecognized_setting_ids[] = $setting_id; - } elseif ( ! current_user_can( $setting->capability ) ) { - $unauthorized_setting_ids[] = $setting_id; - } elseif ( array_key_exists( 'value', $setting_params ) ) { - $unsanitized_values[ $setting_id ] = $setting_params['value']; - } - } - - // Remove values that are unrecognized or unauthorized. - $unsanitized_values = wp_array_slice_assoc( - $unsanitized_values, - array_diff( - array_keys( $unsanitized_values ), - array_merge( $unrecognized_setting_ids, $unauthorized_setting_ids ) - ) - ); - - $invalid_setting_ids = array(); - if ( empty( $options['skip_validation'] ) ) { - // Validate. - if ( method_exists( $customize_manager, 'validate_setting_values' ) ) { - $result['validities'] = $customize_manager->validate_setting_values( $unsanitized_values ); - } else { - // @codeCoverageIgnoreStart - $result['validities'] = array_map( - function( $sanitized ) { - if ( is_null( $sanitized ) ) { - return new \WP_Error( 'invalid_value', __( 'Invalid value', 'customize-snapshots' ) ); - } else { - return true; - } - }, - $unsanitized_values - ); - // @codeCoverageIgnoreEnd - } - $invalid_setting_ids = array_keys( array_filter( $result['validities'], function( $validity ) { - return is_wp_error( $validity ); - } ) ); - } - - // Sanitize. - foreach ( $unsanitized_values as $setting_id => $unsanitized_value ) { - $setting = $customize_manager->get_setting( $setting_id ); - if ( $setting ) { - $result['sanitized'][ $setting_id ] = $setting->sanitize( $unsanitized_value ); - } else { - $unrecognized_setting_ids[] = $setting_id; - } - } - - // Add errors. - if ( ! empty( $unauthorized_setting_ids ) ) { - $error->add( - 'unauthorized_settings', - /* translators: %s is the list of unauthorized setting ids */ - sprintf( __( 'Unauthorized settings: %s', 'customize-snapshots' ), join( ',', $unauthorized_setting_ids ) ), - array( - 'setting_ids' => $unauthorized_setting_ids, - ) - ); - } - if ( ! empty( $unrecognized_setting_ids ) ) { - $error->add( - 'unrecognized_settings', - /* translators: %s is the list of unrecognized setting ids */ - sprintf( __( 'Unrecognized settings: %s', 'customize-snapshots' ), join( ',', $unrecognized_setting_ids ) ), - array( - 'setting_ids' => $unrecognized_setting_ids, - ) - ); - } - if ( 0 !== count( $invalid_setting_ids ) ) { - $code = 'invalid_values'; - $message = __( 'Invalid values', 'customize-snapshots' ); - $error->add( $code, $message, compact( 'invalid_setting_ids' ) ); - } - - if ( ! empty( $error->errors ) ) { - $result['errors'] = $error; - } else { - /* - * Note that somewhat unintuitively the unsanitized post values - * ($unsanitized_values) are stored as opposed to storing the - * sanitized ones ($result['sanitized']). It is still safe to do this - * because they have passed sanitization and validation here. The - * reason why we need to store the raw unsanitized values is so that - * the values can be re-populated into the post values for running - * through the sanitize, validate, and ultimately update logic. - * Once a value has gone through the sanitize logic, it may not be - * suitable for populating into a post value, especially widget - * instances which get exported with a JS value that has the instance - * data encoded, serialized, and hashed to prevent mutation. A - * sanitize filter for a widget instance will convert an encoded - * instance value into a regular instance array, and if this regular - * instance array is placed back into a post value, it will get - * rejected by the sanitize logic for not being an encoded value. - */ - foreach ( $settings_data as $setting_id => $setting_params ) { - if ( ! isset( $this->data[ $setting_id ] ) ) { - $this->data[ $setting_id ] = array(); - } - if ( ! array_key_exists( 'value', $setting_params ) ) { - $setting_params['value'] = null; - } - $this->data[ $setting_id ] = array_merge( $this->data[ $setting_id ], $setting_params ); - } - } - - return $result; - } - - /** - * Persist the data in the snapshot post content. - * - * @param array $args Args. - * @return true|\WP_Error - */ - public function save( array $args ) { - - /** - * Filter the snapshot's data before it's saved to 'post_content'. - * - * @param array $data Customizer snapshot data, with setting IDs mapped to an array - * containing a `value` array item and potentially other metadata. - * @param Customize_Snapshot $this Snapshot object. - */ - $this->data = apply_filters( 'customize_snapshot_save', $this->data, $this ); - - $result = $this->snapshot_manager->post_type->save( array_merge( - $args, - array( - 'uuid' => $this->uuid, - 'data' => $this->data, - 'theme' => $this->snapshot_manager->customize_manager->get_stylesheet(), - ) - ) ); - - if ( ! is_wp_error( $result ) ) { - $this->post_id = $result; - } - - return $result; - } - - /** - * Return whether the snapshot was saved (created/inserted) yet. - * - * @return bool - */ - public function saved() { - return ! is_null( $this->post() ); - } -} diff --git a/php/class-customize-snapshot-command.php b/php/class-customize-snapshot-command.php deleted file mode 100644 index e2103afe..00000000 --- a/php/class-customize-snapshot-command.php +++ /dev/null @@ -1,61 +0,0 @@ -plugin->compat ) { - \WP_CLI::error( __( 'You\'re using older WordPress version please upgrade 4.7 or above to migrate.', 'customize-snapshots' ) ); - return; - } - if ( $migrate_obj->is_migrated() ) { - \WP_CLI::success( __( 'Already migrated.', 'customize-snapshots' ) ); - return; - } - $dry_mode = isset( $assoc_args['dry-run'] ); - if ( ! $dry_mode ) { - wp_suspend_cache_addition( true ); - $post_count = $migrate_obj->changeset_migrate(); - } else { - $ids = $migrate_obj->changeset_migrate( -1, true ); - \WP_CLI::success( __( 'Posts migrated:', 'customize-snapshots' ) . ' ' . implode( ',', $ids ) ); - $post_count = count( $ids ); - } - /* translators: %s: post count.*/ - \WP_CLI::success( sprintf( __( 'Total posts migrated: %s', 'customize-snapshots' ), $post_count ) ); - } -} - -if ( defined( 'WP_CLI' ) && WP_CLI ) { - \WP_CLI::add_command( 'customize-snapshots', __NAMESPACE__ . '\\Customize_Snapshot_Command' ); -} diff --git a/php/class-customize-snapshot-manager-back-compat.php b/php/class-customize-snapshot-manager-back-compat.php deleted file mode 100644 index 78c23c6c..00000000 --- a/php/class-customize-snapshot-manager-back-compat.php +++ /dev/null @@ -1,1030 +0,0 @@ -post_type = new Post_Type_Back_Compat( $this ); - - add_filter( 'customize_refresh_nonces', array( $this, 'filter_customize_refresh_nonces' ) ); - add_action( 'template_redirect', array( $this, 'show_theme_switch_error' ) ); - add_action( 'customize_save_after', array( $this, 'publish_snapshot_with_customize_save_after' ) ); - add_action( 'transition_post_status', array( $this, 'save_settings_with_publish_snapshot' ), 10, 3 ); - add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'handle_update_snapshot_request' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); - add_action( 'customize_save', array( $this, 'check_customize_publish_authorization' ), 10, 0 ); - $this->hooks(); - if ( $this->read_current_snapshot_uuid() ) { - $this->load_snapshot(); - } elseif ( is_customize_preview() && isset( $_REQUEST['wp_customize_preview_ajax'] ) && 'true' === $_REQUEST['wp_customize_preview_ajax'] ) { // WPCS: input var ok; CSRF ok. - add_action( 'wp_loaded', array( $this, 'setup_preview_ajax_requests' ), 12 ); - } - } - - /** - * Get the Customize_Snapshot instance. - * - * @return Customize_Snapshot_Back_Compat - */ - public function snapshot() { - return $this->snapshot; - } - - /** - * Ensure Customizer manager is instantiated. - * - * @global \WP_Customize_Manager $wp_customize - */ - public function ensure_customize_manager() { - global $wp_customize; - if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $wp_customize = new \WP_Customize_Manager(); // WPCS: override ok. - } - $this->customize_manager = $wp_customize; - } - - /** - * Include the snapshot nonce in the Customizer nonces. - * - * @param array $nonces Nonces. - * @return array Nonces. - */ - public function filter_customize_refresh_nonces( $nonces ) { - $nonces['snapshot'] = wp_create_nonce( self::AJAX_ACTION ); - return $nonces; - } - - /** - * Enqueue styles & scripts for the Customizer. - * - * @action customize_controls_enqueue_scripts - * @global \WP_Customize_Manager $wp_customize - */ - public function enqueue_controls_scripts() { - $this->ensure_customize_manager(); - - wp_enqueue_style( 'customize-snapshots' ); - wp_enqueue_script( 'customize-snapshots-compat' ); - - if ( $this->snapshot ) { - $post = $this->snapshot->post(); - $this->override_post_date_default_data( $post ); - $preview_url_query_vars = $this->post_type->get_customizer_state_query_vars( $post->ID ); - } - - // Script data array. - $exports = apply_filters( 'customize_snapshots_export_data', array( - 'action' => self::AJAX_ACTION, - 'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(), - 'editLink' => isset( $post ) ? get_edit_post_link( $post, 'raw' ) : '', - 'publishDate' => isset( $post->post_date ) ? $post->post_date : '', - 'title' => isset( $post->post_title ) ? $post->post_title : '', - 'postStatus' => isset( $post->post_status ) ? $post->post_status : '', - 'currentUserCanPublish' => current_user_can( 'customize_publish' ), - 'initialServerDate' => current_time( 'mysql', false ), - 'initialServerTimestamp' => floor( microtime( true ) * 1000 ), - 'theme' => $this->original_stylesheet, - 'previewingTheme' => isset( $preview_url_query_vars['theme'] ) ? $preview_url_query_vars['theme'] : '', - 'i18n' => array( - 'saveButton' => __( 'Save', 'customize-snapshots' ), - 'updateButton' => __( 'Update', 'customize-snapshots' ), - 'scheduleButton' => __( 'Schedule', 'customize-snapshots' ), - 'submit' => __( 'Submit', 'customize-snapshots' ), - 'submitted' => __( 'Submitted', 'customize-snapshots' ), - 'publish' => __( 'Publish', 'customize-snapshots' ), - 'published' => __( 'Published', 'customize-snapshots' ), - 'permsMsg' => array( - 'save' => __( 'You do not have permission to publish changes, but you can create a snapshot by clicking the "Save" button.', 'customize-snapshots' ), - 'update' => __( 'You do not have permission to publish changes, but you can modify this snapshot by clicking the "Update" button.', 'customize-snapshots' ), - ), - 'errorMsg' => __( 'The snapshot could not be saved.', 'customize-snapshots' ), - 'errorTitle' => __( 'Error', 'customize-snapshots' ), - 'collapseSnapshotScheduling' => __( 'Collapse snapshot scheduling', 'customize-snapshots' ), - 'expandSnapshotScheduling' => __( 'Expand snapshot scheduling', 'customize-snapshots' ), - ), - 'snapshotExists' => ( $this->snapshot && $this->snapshot->saved() ), - ) ); - - wp_scripts()->add_inline_script( - 'customize-snapshots-compat', - sprintf( 'var _customizeSnapshotsCompatSettings = %s;', wp_json_encode( $exports ) ), - 'before' - ); - } - - /** - * Load snapshot. - */ - public function load_snapshot() { - $this->ensure_customize_manager(); - $this->snapshot = new Customize_Snapshot_Back_Compat( $this, $this->current_snapshot_uuid ); - - if ( ! $this->should_import_and_preview_snapshot( $this->snapshot ) ) { - return; - } - - $this->add_widget_setting_preview_filters(); - $this->add_nav_menu_setting_preview_filters(); - - /* - * Populate post values. - * - * Note we have to defer until setup_theme since the transaction - * can be set beforehand, and wp_magic_quotes() would not have - * been called yet, resulting in a $_POST['customized'] that is - * double-escaped. Note that this happens at priority 1, which - * is immediately after Customize_Snapshot_Manager::store_customized_post_data - * which happens at setup_theme priority 0, so that the initial - * POST data can be preserved. - */ - if ( did_action( 'setup_theme' ) ) { - $this->import_snapshot_data(); - } else { - add_action( 'setup_theme', array( $this, 'import_snapshot_data' ) ); - } - - // Block the robots. - add_action( 'wp_head', 'wp_no_robots' ); - - // Preview post values. - if ( did_action( 'wp_loaded' ) ) { - $this->preview_snapshot_settings(); - } else { - add_action( 'wp_loaded', array( $this, 'preview_snapshot_settings' ), 11 ); - } - } - - /** - * Populate post values and $_POST['customized'] wth the snapshot's data. - * - * Plugins used to have to dynamically register settings by inspecting the - * $_POST['customized'] var and manually re-parse and inspect to see if it - * contains settings that wouldn't be registered otherwise. This ensures - * that these plugins will continue to work. - * - * Note that this can't be called prior to the setup_theme action or else - * magic quotes may end up getting added twice. - * - * @see Customize_Snapshot_Manager::should_import_and_preview_snapshot() - */ - public function import_snapshot_data() { - /* - * We don't merge the snapshot data with any existing existing unsanitized - * post values since should_import_and_preview_snapshot returns false if - * there is any existing data in the Customizer state. This is to prevent - * clobbering existing values (or previewing non-snapshotted values on frontend). - * Note that wp.customize.Snapshots.extendPreviewerQuery() will extend the - * previewer data to include the current snapshot UUID. - */ - $snapshot_values = array_filter( - wp_list_pluck( $this->snapshot->data(), 'value' ), - function( $value ) { - return ! is_null( $value ); - } - ); - - // Populate input vars for back-compat. - $_POST['customized'] = wp_slash( wp_json_encode( $snapshot_values ) ); - // @codingStandardsIgnoreStart - $_REQUEST['customized'] = $_POST['customized']; - // @codingStandardsIgnoreEnd - - foreach ( $snapshot_values as $setting_id => $value ) { - $this->customize_manager->set_post_value( $setting_id, $value ); - } - } - - /** - * Determine whether the current snapshot can be previewed. - * - * @param Customize_Snapshot_Back_Compat $snapshot Snapshot to check. - * @return true|\WP_Error Returns true if previewable, or `WP_Error` if cannot. - */ - public function should_import_and_preview_snapshot( Customize_Snapshot_Back_Compat $snapshot ) { - global $pagenow; - - // Ignore if in the admin, but not Admin Ajax or Customizer. - if ( is_admin() && ! in_array( $pagenow, array( 'admin-ajax.php', 'customize.php' ), true ) ) { - return false; - } - - if ( is_wp_error( $this->get_theme_switch_error( $snapshot ) ) ) { - return false; - } - - // Abort if doing customize_save. - if ( $this->doing_customize_save_ajax() ) { - return false; - } - - // Abort if the snapshot was already published. - if ( $snapshot->saved() && 'publish' === get_post_status( $snapshot->post() ) ) { - return false; - } - - /* - * Prevent clobbering existing values (or previewing non-snapshotted values on frontend). - * Note that wp.customize.Snapshots.extendPreviewerQuery() will extend the - * previewer data to include the current snapshot UUID. - */ - if ( $this->customize_manager && count( $this->customize_manager->unsanitized_post_values() ) > 0 ) { - return false; - } - - return true; - } - - /** - * Redirect when preview is not allowed for the current theme. - * - * @param Customize_Snapshot $snapshot Snapshot to check. - * @return \WP_Error|null - */ - public function get_theme_switch_error( Customize_Snapshot $snapshot ) { - - // Loading a snapshot into the context of a theme switch is not supported. - if ( ! $this->is_theme_active() ) { - return new \WP_Error( 'snapshot_theme_switch', __( 'Snapshot cannot be previewed when previewing a theme switch.', 'customize-snapshots' ) ); - } - - $snapshot_post = $snapshot->post(); - if ( ! $snapshot_post ) { - return null; - } - - $snapshot_theme = get_post_meta( $snapshot_post->ID, '_snapshot_theme', true ); - if ( ! empty( $snapshot_theme ) && get_stylesheet() !== $snapshot_theme ) { - return new \WP_Error( 'snapshot_theme_switched', __( 'Snapshot requested was made for a different theme and cannot be previewed with the current theme.', 'customize-snapshots' ) ); - } - - return null; - } - - /** - * Is previewing settings. - * - * Plugins and themes may currently only use `is_customize_preview()` to - * decide whether or not they can store a value in the object cache. For - * example, see `Twenty_Eleven_Ephemera_Widget::widget()`. However, when - * viewing a snapshot on the frontend, the `is_customize_preview()` method - * will return `false`. Plugins and themes that store values in the object - * cache must either skip doing this when `$this->previewing` is `true`, - * or include the `$this->current_snapshot_uuid` (`current_snapshot_uuid()`) - * in the cache key when it is `true`. Note that if the `customize_preview_init` action - * was done, this means that the settings have been previewed in the regular - * Customizer preview. - * - * @see Twenty_Eleven_Ephemera_Widget::widget() - * @see WP_Customize_Manager::is_previewing_settings() - * @see is_previewing_settings() - * @see current_snapshot_uuid()() - * @see WP_Customize_Manager::customize_preview_init() - * @see Customize_Snapshot_Manager::$previewing_settings - * - * @return bool Whether previewing settings. - */ - public function is_previewing_settings() { - return $this->previewing_settings || did_action( 'customize_preview_init' ); - } - - /** - * Preview the snapshot settings. - * - * Note that this happens at `wp_loaded` action with priority 11 so that we - * can look at whether the `customize_preview_init` action was done. - */ - public function preview_snapshot_settings() { - if ( $this->is_previewing_settings() ) { - return; - } - $this->previewing_settings = true; - - /* - * Note that we need to preview the settings outside the Customizer preview - * and in the Customizer pane itself so we can load a previous snapshot - * into the Customizer. We have to prevent the previews from being added - * in the case of a customize_save action because then update_option() - * may short-circuit because it will detect that there are no changes to - * make. - */ - foreach ( $this->snapshot->settings() as $setting ) { - $setting->preview(); - $setting->dirty = true; - } - } - - /** - * Add filters for previewing widgets on the frontend. - */ - function add_widget_setting_preview_filters() { - /* - * Add WP_Customize_Widget component hooks which were short-circuited in 4.5 (r36611 for #35895). - * See https://core.trac.wordpress.org/ticket/35895 - */ - if ( isset( $this->customize_manager->widgets ) && ! current_user_can( 'edit_theme_options' ) ) { - $hooks = array( - 'customize_dynamic_setting_args' => array( - 'callback' => array( $this->customize_manager->widgets, 'filter_customize_dynamic_setting_args' ), - 'priority' => 10, - ), - 'widgets_init' => array( - 'callback' => array( $this->customize_manager->widgets, 'register_settings' ), - 'priority' => 95, - ), - 'customize_register' => array( - 'callback' => array( $this->customize_manager->widgets, 'schedule_customize_register' ), - 'priority' => 1, - ), - ); - foreach ( $hooks as $hook_name => $hook_args ) { - // Note that add_action()/has_action() are just aliases for add_filter()/has_filter(). - if ( ! has_filter( $hook_name, $hook_args['callback'] ) ) { - add_filter( $hook_name, $hook_args['callback'], $hook_args['priority'], PHP_INT_MAX ); - } - } - } - - /* - * Disable routine which fails because \WP_Customize_Manager::setup_theme() is - * never called in a frontend preview context, whereby the original_stylesheet - * is never set and so \WP_Customize_Manager::is_theme_active() will thus - * always return true because get_stylesheet() !== null. - * - * The action being removed is responsible for adding an option_sidebar_widgets - * filter \WP_Customize_Widgets::filter_option_sidebars_widgets_for_theme_switch() - * which causes the sidebars_widgets to be overridden with a global variable. - */ - if ( ! is_admin() ) { - remove_action( 'wp_loaded', array( $this->customize_manager->widgets, 'override_sidebars_widgets_for_theme_switch' ) ); - } - } - - /** - * Add filters for previewing nav menus on the frontend. - */ - public function add_nav_menu_setting_preview_filters() { - if ( isset( $this->customize_manager->nav_menus ) && ! current_user_can( 'edit_theme_options' ) ) { - $hooks = array( - 'customize_register' => array( - 'callback' => array( $this->customize_manager->nav_menus, 'customize_register' ), - 'priority' => 11, - ), - 'customize_dynamic_setting_args' => array( - 'callback' => array( $this->customize_manager->nav_menus, 'filter_dynamic_setting_args' ), - 'priority' => 10, - ), - 'customize_dynamic_setting_class' => array( - 'callback' => array( $this->customize_manager->nav_menus, 'filter_dynamic_setting_class' ), - 'priority' => 10, - ), - 'wp_nav_menu_args' => array( - 'callback' => array( $this->customize_manager->nav_menus, 'filter_wp_nav_menu_args' ), - 'priority' => 1000, - ), - 'wp_nav_menu' => array( - 'callback' => array( $this->customize_manager->nav_menus, 'filter_wp_nav_menu' ), - 'priority' => 10, - ), - ); - foreach ( $hooks as $hook_name => $hook_args ) { - // Note that add_action()/has_action() are just aliases for add_filter()/has_filter(). - if ( ! has_filter( $hook_name, $hook_args['callback'] ) ) { - add_filter( $hook_name, $hook_args['callback'], $hook_args['priority'], PHP_INT_MAX ); - } - } - } - - if ( isset( $this->customize_manager->nav_menus ) ) { - add_action( 'customize_register', array( $this, 'preview_early_nav_menus_in_customizer' ), 9 ); - } - } - - /** - * Preview nav menu settings early so that the sections and controls for snapshot values will be added properly. - * - * This must happen at `customize_register` priority prior to 11 which is when `WP_Customize_Nav_Menus::customize_register()` runs. - * This is only relevant when accessing the Customizer app (customize.php), as this is where sections/controls matter. - * - * @see \WP_Customize_Nav_Menus::customize_register() - */ - public function preview_early_nav_menus_in_customizer() { - if ( ! is_admin() ) { - return; - } - $this->customize_manager->add_dynamic_settings( array_keys( $this->snapshot()->data() ) ); - foreach ( $this->snapshot->settings() as $setting ) { - $is_nav_menu_setting = ( - $setting instanceof \WP_Customize_Nav_Menu_Setting - || - $setting instanceof \WP_Customize_Nav_Menu_Item_Setting - || - preg_match( '/^nav_menu_locations\[/', $setting->id ) - ); - if ( $is_nav_menu_setting ) { - $setting->preview(); - - /* - * The following is redundant because it will be done later in - * Customize_Snapshot_Manager::preview_snapshot_settings(). - * Also note that the $setting instance here will likely be - * blown away inside of WP_Customize_Nav_Menus::customize_register(), - * when add_setting is called there. What matters here is that - * preview() is called on the setting _before_ the logic inside - * WP_Customize_Nav_Menus::customize_register() runs, so that - * the nav menu sections will be created. - */ - $setting->dirty = true; - } - } - } - - /** - * Setup previewing of Ajax requests in the Customizer preview. - * - * @global \WP_Customize_Manager $wp_customize - */ - public function setup_preview_ajax_requests() { - global $wp_customize, $pagenow; - - /* - * When making admin-ajax requests from the frontend, settings won't be - * previewed because is_admin() and the call to preview will be - * short-circuited in \WP_Customize_Manager::wp_loaded(). - */ - if ( ! did_action( 'customize_preview_init' ) ) { - $wp_customize->customize_preview_init(); - } - - // Note that using $pagenow is easier to test vs DOING_AJAX. - if ( ! empty( $pagenow ) && 'admin-ajax.php' === $pagenow ) { - $this->override_request_method(); - } else { - add_action( 'parse_request', array( $this, 'override_request_method' ), 5 ); - } - - $wp_customize->remove_preview_signature(); - } - - /** - * Attempt to convert the current request environment into another environment. - * - * @global \WP $wp - * - * @return bool Whether the override was applied. - */ - public function override_request_method() { - global $wp; - - // Skip of X-HTTP-Method-Override request header is not present. - if ( ! isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) || ! isset( $_SERVER['REQUEST_METHOD'] ) ) { // WPCS: input var ok. - return false; - } - - // Skip if REST API request since it has built-in support for overriding the request method. - if ( ! empty( $wp ) && ! empty( $wp->query_vars['rest_route'] ) ) { - return false; - } - - // Skip if the request method is not GET or POST, or the override is the same as the original. - $original_request_method = strtoupper( sanitize_key( $_SERVER['REQUEST_METHOD'] ) ); // WPCS: input var ok. - $override_request_method = strtoupper( sanitize_key( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ); // WPCS: input var ok. - if ( ! in_array( $override_request_method, array( 'GET', 'POST' ), true ) || $original_request_method === $override_request_method ) { - return false; - } - - // Convert a POST request into a GET request. - if ( 'GET' === $override_request_method && 'POST' === $original_request_method ) { - $_SERVER['REQUEST_METHOD'] = $override_request_method; - $_GET = array_merge( $_GET, $_POST ); // WPCS: input var ok; CSRF ok. - $_SERVER['QUERY_STRING'] = build_query( array_map( 'rawurlencode', wp_unslash( $_GET ) ) ); // WPCS: input var ok. CSRF ok. - return true; - } - - return false; - } - - /** - * Check whether customize_publish capability is granted in customize_save. - */ - public function check_customize_publish_authorization() { - if ( $this->doing_customize_save_ajax() && ! current_user_can( 'customize_publish' ) ) { - wp_send_json_error( array( - 'error' => 'customize_publish_unauthorized', - ) ); - } - } - - /** - * Show the theme switch error if there is one. - */ - public function show_theme_switch_error() { - if ( empty( $this->snapshot ) ) { - return; - } - $error = $this->get_theme_switch_error( $this->snapshot ); - if ( is_wp_error( $error ) ) { - wp_die( esc_html( $error->get_error_message() ) ); - } - } - - /** - * Publish the snapshot snapshots via AJAX. - * - * Fires at `customize_save_after` to update and publish the snapshot. - * The logic in here is the inverse of save_settings_with_publish_snapshot. - * - * @see Customize_Snapshot_Manager::save_settings_with_publish_snapshot() - * - * @return bool Whether the snapshot was saved successfully. - */ - public function publish_snapshot_with_customize_save_after() { - $that = $this; - - if ( ! $this->snapshot || ! $this->doing_customize_save_ajax() ) { - return false; - } - - // This should never be reached due to Customize_Snapshot_Manager::check_customize_publish_authorization(). - if ( ! current_user_can( 'customize_publish' ) ) { - return false; - } - - $settings_data = array_map( - function( $value ) { - return compact( 'value' ); - }, - $this->customize_manager->unsanitized_post_values() - ); - $result = $this->snapshot->set( $settings_data, array( - 'skip_validation' => true, - ) ); - if ( ! empty( $result['errors'] ) ) { - add_filter( 'customize_save_response', function( $response ) use ( $result, $that ) { - $response['snapshot_errors'] = $that->prepare_errors_for_response( $result['errors'] ); - return $response; - } ); - return false; - } - - if ( ! $this->snapshot->post() || 'publish' !== $this->snapshot->post()->post_status ) { - $args = array( - 'status' => 'publish', - ); - - // Ensure a scheduled Snapshot is published. - if ( $this->snapshot->post() && 'future' === $this->snapshot->post()->post_status ) { - $args['edit_date'] = true; - $args['date_gmt'] = current_time( 'mysql', true ); - } - - if ( isset( $_POST['title'] ) ) { // WPCS: input var ok. CSRF ok because customize_save_after happens after nonce check. - $title = sanitize_text_field( wp_unslash( $_POST['title'] ) ); // WPCS: Input var ok. CSRF ok because customize_save_after happens after nonce check. - if ( ! empty( $title ) ) { - $args['post_title'] = $title; - } - } - - $r = $this->snapshot->save( $args ); - if ( is_wp_error( $r ) ) { - add_filter( 'customize_save_response', function( $response ) use ( $r, $that ) { - $response['snapshot_errors'] = $that->prepare_errors_for_response( $r ); - return $response; - } ); - return false; - } - } - - // Send the new UUID to the client for the next snapshot. - $class = __CLASS__; // For PHP 5.3. - add_filter( 'customize_save_response', function( $data ) use ( $class ) { - $data['new_customize_snapshot_uuid'] = $class::generate_uuid(); - return $data; - } ); - return true; - } - - /** - * Publish snapshot changes when snapshot post is being published. - * - * The logic in here is the inverse of to publish_snapshot_with_customize_save_after. - * - * The meat of the logic that manipulates the post_content and validates the settings - * needs to be done in wp_insert_post_data filter in like a - * filter_insert_post_data_to_validate_published_snapshot method? This would - * have the benefit of reducing one wp_insert_post() call. - * - * @todo Consider using wp_insert_post_data to prevent double calls to wp_insert_post(). - * @see Customize_Snapshot_Manager::publish_snapshot_with_customize_save_after() - * - * @param string $new_status New status. - * @param string $old_status Old status. - * @param \WP_Post $post Post object. - * @return bool Whether the settings were saved. - */ - public function save_settings_with_publish_snapshot( $new_status, $old_status, $post ) { - - // Abort if not transitioning a snapshot post to publish from a non-publish status. - if ( $this->get_post_type() !== $post->post_type || 'publish' !== $new_status || $new_status === $old_status ) { - return false; - } - - $this->ensure_customize_manager(); - - if ( $this->doing_customize_save_ajax() ) { - // Short circuit because customize_save ajax call is changing status. - return false; - } - - // Populate customized state with values from snapshot. - $snapshot_content = $this->post_type->get_post_content( $post ); - foreach ( $snapshot_content as $setting_id => $setting_params ) { - if ( array_key_exists( 'value', $setting_params ) ) { - $this->customize_manager->set_post_value( $setting_id, $setting_params['value'] ); - } - } - - if ( ! did_action( 'customize_register' ) ) { - /* - * When running from CLI or Cron, we have to remove the action because - * it will get added with a default priority of 10, after themes and plugins - * have already done add_action( 'customize_register' ), resulting in them - * being called first at the priority 10. So we manually call the - * prerequisite function WP_Customize_Manager::register_controls() and - * remove it from being called when the customize_register action fires. - */ - remove_action( 'customize_register', array( $this->customize_manager, 'register_controls' ) ); - $this->customize_manager->register_controls(); - - /* - * Unfortunate hack to prevent \WP_Customize_Widgets::customize_register() - * from calling preview() on settings. This needs to be cleaned up in core. - * It is important for previewing to be prevented because if an option has - * a filter it will short-circuit when an update is attempted since it - * detects that there is no change to be put into the DB. - * See: https://github.com/xwp/wordpress-develop/blob/e8c58c47db1421a1d0b2afa9ad4b9eb9e1e338e0/src/wp-includes/class-wp-customize-widgets.php#L208-L217 - */ - if ( ! defined( 'DOING_AJAX' ) ) { - define( 'DOING_AJAX', true ); - } - $_REQUEST['action'] = 'customize_save'; - - /** This action is documented in wp-includes/class-wp-customize-manager.php */ - do_action( 'customize_register', $this->customize_manager ); - - // undefine( 'DOING_AJAX' )... just kidding. This is the end of the unfortunate hack and it should be fixed in Core. - unset( $_REQUEST['action'] ); // WPCS: Input var ok. - } - - if ( method_exists( $this->customize_manager, 'validate_setting_values' ) ) { - /** This action is documented in wp-includes/class-wp-customize-manager.php */ - do_action( 'customize_save_validation_before', $this->customize_manager ); - } - - $setting_ids = array_keys( $snapshot_content ); - $this->customize_manager->add_dynamic_settings( $setting_ids ); - - /** This action is documented in wp-includes/class-wp-customize-manager.php */ - do_action( 'customize_save', $this->customize_manager ); - - /** - * Settings to save. - * - * @var \WP_Customize_Setting[] - */ - $settings = array(); - - $publish_error_count = 0; - foreach ( $snapshot_content as $setting_id => &$setting_params ) { - - // Missing value error. - if ( ! isset( $setting_params['value'] ) || is_null( $setting_params['value'] ) ) { - if ( ! is_array( $setting_params ) ) { - if ( ! empty( $setting_params ) ) { - $setting_params = array( - 'value' => $setting_params, - ); - } else { - $setting_params = array(); - } - } - $setting_params['publish_error'] = 'null_value'; - $publish_error_count += 1; - continue; - } - - // Unrecognized setting error. - $setting = $this->customize_manager->get_setting( $setting_id ); - if ( ! ( $setting instanceof \WP_Customize_Setting ) ) { - $setting_params['publish_error'] = 'unrecognized_setting'; - $publish_error_count += 1; - continue; - } - - // Validate setting value. - if ( method_exists( $setting, 'validate' ) ) { - $validity = $setting->validate( $setting_params['value'] ); - if ( is_wp_error( $validity ) ) { - $setting_params['publish_error'] = $validity->get_error_code(); - $publish_error_count += 1; - continue; - } - } - - // Validate sanitized setting value. - $sanitized_value = $setting->sanitize( $setting_params['value'] ); - if ( is_null( $sanitized_value ) || is_wp_error( $sanitized_value ) ) { - $setting_params['publish_error'] = is_wp_error( $sanitized_value ) ? $sanitized_value->get_error_code() : 'invalid_value'; - $publish_error_count += 1; - continue; - } - - $settings[] = $setting; - unset( $setting_params['publish_error'] ); - } // End foreach(). - - // Handle error scenarios. - if ( $publish_error_count > 0 ) { - $update_setting_args = array( - 'ID' => $post->ID, - 'post_content' => Customize_Snapshot_Manager::encode_json( $snapshot_content ), - 'post_status' => 'pending', - ); - wp_update_post( wp_slash( $update_setting_args ) ); - update_post_meta( $post->ID, 'snapshot_error_on_publish', $publish_error_count ); - - add_filter( 'redirect_post_location', function( $location ) { - $location = add_query_arg( 'snapshot_error_on_publish', '1', $location ); - return $location; - } ); - return false; - } - - /* - * Change all setting capabilities temporarily to 'exist' to allow them to - * be saved regardless of current user, such as when WP-Cron is publishing - * the snapshot post if it was scheduled. It is safe to do this because - * a setting can only be written into a snapshot by users who have the - * capability, so after it has been added to a snapshot it is good to commit. - */ - $existing_caps = wp_list_pluck( $settings, 'capability' ); - foreach ( $settings as $setting ) { - $setting->capability = 'exist'; - } - - // Persist the settings in the DB. - foreach ( $settings as $setting ) { - $setting->save(); - } - - // Restore setting capabilities. - foreach ( $existing_caps as $setting_id => $existing_cap ) { - $settings[ $setting_id ]->capability = $existing_cap; - } - - /** This action is documented in wp-includes/class-wp-customize-manager.php */ - do_action( 'customize_save_after', $this->customize_manager ); - - // Remove any previous error on setting. - delete_post_meta( $post->ID, 'snapshot_error_on_publish' ); - - return true; - } - - /** - * Update snapshots via AJAX. - */ - public function handle_update_snapshot_request() { - 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' ); - } elseif ( empty( $this->current_snapshot_uuid ) ) { - status_header( 400 ); - wp_send_json_error( 'invalid_customize_snapshot_uuid' ); - } elseif ( 0 === count( $this->customize_manager->unsanitized_post_values() ) ) { - status_header( 400 ); - wp_send_json_error( 'missing_snapshot_customized' ); - } - - if ( isset( $_POST['status'] ) ) { // WPCS: input var ok. - $status = sanitize_key( $_POST['status'] ); // WPCS: input var ok. - } else { - $status = 'draft'; - } - if ( ! in_array( $status, array( 'draft', 'pending', 'future' ), true ) ) { - status_header( 400 ); - wp_send_json_error( 'bad_status' ); - } - if ( 'future' === $status && ! current_user_can( 'customize_publish' ) ) { - status_header( 400 ); - wp_send_json_error( 'customize_not_allowed' ); - } - $publish_date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : ''; // WPCS: input var ok. - if ( 'future' === $status ) { - $publish_date_obj = new \DateTime( $publish_date ); - $current_date = new \DateTime( current_time( 'mysql' ) ); - if ( empty( $publish_date ) || ! $publish_date_obj || $current_date > $publish_date_obj ) { - status_header( 400 ); - wp_send_json_error( 'bad_schedule_time' ); - } - } - - // Prevent attempting to modify a "locked" snapshot (a published one). - $post = $this->snapshot->post(); - if ( $post && 'publish' === $post->post_status ) { - wp_send_json_error( array( - 'errors' => array( - 'already_published' => array( - 'message' => __( 'The snapshot has already published so it is locked.', 'customize-snapshots' ), - ), - ), - ) ); - } - - /** - * Add any additional checks before saving snapshot. - * - * @param Customize_Snapshot $snapshot Snapshot to be saved. - * @param Customize_Snapshot_Manager $snapshot_manager Snapshot manager. - */ - do_action( 'customize_snapshot_save_before', $this->snapshot, $this ); - - // Set the snapshot UUID. - $post_type = get_post_type_object( $this->get_post_type() ); - $authorized = ( $post ? - current_user_can( $post_type->cap->edit_post, $post->ID ) : - current_user_can( 'customize' ) - ); - if ( ! $authorized ) { - status_header( 403 ); - wp_send_json_error( 'unauthorized' ); - } - - $data = array( - 'errors' => null, - ); - $settings_data = array_map( - function( $value ) { - return compact( 'value' ); - }, - $this->customize_manager->unsanitized_post_values() - ); - $r = $this->snapshot->set( $settings_data ); - if ( method_exists( $this->customize_manager, 'prepare_setting_validity_for_js' ) ) { - $data['setting_validities'] = array_map( - array( $this->customize_manager, 'prepare_setting_validity_for_js' ), - $r['validities'] - ); - } - - if ( $r['errors'] ) { - $data['errors'] = $this->prepare_errors_for_response( $r['errors'] ); - wp_send_json_error( $data ); - } - $args = array( - 'status' => $status, - ); - if ( isset( $_POST['title'] ) ) { // WPCS: input var ok. - $title = sanitize_text_field( wp_unslash( $_POST['title'] ) ); // WPCS: input var ok. - if ( '' !== $title ) { - $args['post_title'] = $title; - } - } - - if ( isset( $publish_date_obj ) && 'future' === $status ) { - $args['date_gmt'] = get_gmt_from_date( $publish_date_obj->format( 'Y-m-d H:i:s' ) ); - } - $r = $this->snapshot->save( $args ); - - $post = $this->snapshot->post(); - if ( $post ) { - $data['edit_link'] = get_edit_post_link( $post, 'raw' ); - $data['snapshot_publish_date'] = $post->post_date; - $data['title'] = $post->post_title; - } - - if ( is_wp_error( $r ) ) { - $data['errors'] = $this->prepare_errors_for_response( $r ); - wp_send_json_error( $data ); - } - - /** This filter is documented in wp-includes/class-wp-customize-manager.php */ - $data = apply_filters( 'customize_save_response', $data, $this->customize_manager ); - wp_send_json_success( $data ); - } - - /** - * Set up Customizer preview. - */ - public function customize_preview_init() { - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) ); - } - - /** - * Enqueue Customizer preview scripts. - * - * @global \WP_Customize_Manager $wp_customize - */ - public function enqueue_preview_scripts() { - global $wp_customize; - - $handle = 'customize-snapshots-preview'; - wp_enqueue_script( $handle ); - wp_enqueue_style( $handle ); - - $exports = array( - 'home_url' => wp_parse_url( home_url( '/' ) ), - 'rest_api_url' => wp_parse_url( rest_url( '/' ) ), - 'admin_ajax_url' => wp_parse_url( admin_url( 'admin-ajax.php' ) ), - 'initial_dirty_settings' => array_keys( $wp_customize->unsanitized_post_values() ), - ); - wp_add_inline_script( - $handle, - sprintf( 'CustomizeSnapshotsPreview.init( %s )', wp_json_encode( $exports ) ), - 'after' - ); - } - - /** - * Enqueue Customizer frontend scripts. - */ - public function enqueue_frontend_scripts() { - if ( ! $this->snapshot || is_customize_preview() ) { - return; - } - $handle = 'customize-snapshots-frontend'; - wp_enqueue_script( $handle ); - - $exports = array( - 'uuid' => $this->snapshot ? $this->snapshot->uuid() : null, - 'home_url' => wp_parse_url( home_url( '/' ) ), - 'l10n' => array( - 'restoreSessionPrompt' => __( 'It seems you may have inadvertently navigated away from previewing a customized state. Would you like to restore the snapshot context?', 'customize-snapshots' ), - ), - ); - wp_add_inline_script( - $handle, - sprintf( 'CustomizeSnapshotsFrontend.init( %s )', wp_json_encode( $exports ) ), - 'after' - ); - } - - /** - * Underscore (JS) templates. - */ - public function render_templates() { - $this->add_edit_box_template(); - ?> - - - - - - - - - - post_type, 'init' ) ); + add_filter( 'customize_changeset_branching', '__return_true' ); // For WordPress 4.9. add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - add_action( 'load-edit.php', array( $this, 'handle_frontend_changset_publish' ) ); + add_action( 'load-edit.php', array( $this, 'handle_frontend_changeset_publish' ) ); add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); @@ -129,17 +106,6 @@ function init() { $this->post_type = new Post_Type( $this ); $this->hooks(); add_filter( 'customize_save_response', array( $this, 'add_snapshot_var_to_customize_save' ), 10, 2 ); - if ( $this->read_current_snapshot_uuid() ) { - $this->load_snapshot(); - } - } - - /** - * Load Snapshot. - */ - public function load_snapshot() { - $this->ensure_customize_manager(); - $this->snapshot = new Customize_Snapshot( $this ); } /** @@ -152,33 +118,29 @@ public function load_snapshot() { */ public function add_snapshot_var_to_customize_save( $response, $customize_manager ) { $changeset_post = get_post( $customize_manager->changeset_post_id() ); - $response['edit_link'] = $this->snapshot->get_edit_link( $changeset_post->ID ); + $response['edit_link'] = $this->get_edit_link( $changeset_post->ID ); $response['publish_date'] = $changeset_post->post_date; $response['title'] = $changeset_post->post_title; return $response; } /** - * Read the current snapshot UUID from the request. + * Get edit post link. + * + * @param int|\WP_Post $post_id Post. * - * @returns bool Whether a valid snapshot was read. + * @return null|string Post edit link. */ - public function read_current_snapshot_uuid() { - $customize_arg = $this->get_customize_uuid_param(); - $frontend_arg = $this->get_front_uuid_param(); - $uuid = null; - if ( isset( $_REQUEST[ $customize_arg ] ) ) { // WPCS: input var ok. CSRF ok. - $uuid = sanitize_key( wp_unslash( $_REQUEST[ $customize_arg ] ) ); // WPCS: input var ok. CSRF ok. - } elseif ( isset( $_REQUEST[ $frontend_arg ] ) ) { // WPCS: input var ok. CSRF ok. - $uuid = sanitize_key( wp_unslash( $_REQUEST[ $frontend_arg ] ) ); // WPCS: input var ok. CSRF ok. + public function get_edit_link( $post_id ) { + $has_filter = has_filter( 'get_edit_post_link', '__return_empty_string' ); + if ( $has_filter ) { + remove_filter( 'get_edit_post_link', '__return_empty_string' ); } - - if ( $uuid && static::is_valid_uuid( $uuid ) ) { - $this->current_snapshot_uuid = $uuid; - return true; + $link = get_edit_post_link( $post_id, 'raw' ); + if ( $has_filter ) { + add_filter( 'get_edit_post_link', '__return_empty_string' ); } - $this->current_snapshot_uuid = null; - return false; + return $link; } /** @@ -190,10 +152,21 @@ public function doing_customize_save_ajax() { return isset( $_REQUEST['action'] ) && sanitize_key( wp_unslash( $_REQUEST['action'] ) ) === 'customize_save'; // WPCS: input var ok. CSRF ok. } + /** + * Get the customize manager. + * + * @return \WP_Customize_Manager Manager. + */ + function get_customize_manager() { + global $wp_customize; + return $wp_customize; + } + /** * Ensure Customizer manager is instantiated. * * @global \WP_Customize_Manager $wp_customize + * @return \WP_Customize_Manager Manager. */ public function ensure_customize_manager() { global $wp_customize; @@ -201,19 +174,10 @@ public function ensure_customize_manager() { $args = array(); if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - - if ( null !== $this->current_snapshot_uuid ) { - $args['changeset_uuid'] = $this->current_snapshot_uuid; - } - - if ( null !== $this->stylesheet ) { - $args['theme'] = $this->stylesheet; - } - $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. } - $this->customize_manager = $wp_customize; + return $wp_customize; } /** @@ -222,39 +186,31 @@ public function ensure_customize_manager() { * @return bool Whether theme is active. * * @deprecated in favor of WP_Customize_Manager::is_theme_active() - * @todo move to back compat? */ public function is_theme_active() { - if ( empty( $this->customize_manager ) ) { - return true; - } - return $this->customize_manager->get_stylesheet() === $this->original_stylesheet; + return is_customize_preview() && $this->get_customize_manager()->is_theme_active(); } /** * Add snapshot UUID the Customizer return URL. * - * If the Customizer was loaded with a snapshot UUID, let the return URL include this snapshot. - * - * @todo move to back compat? + * If the Customizer was loaded from a referring URL had a changeset UUID, then ensure the return URL also includes this param. */ public function add_snapshot_uuid_to_return_url() { + $wp_customize = $this->get_customize_manager(); $should_add_snapshot_uuid = ( - isset( $_GET[ $this->get_front_uuid_param() ] ) + is_customize_preview() && - $this->current_snapshot_uuid - && - $this->customize_manager->is_theme_active() - && - false === strpos( $this->customize_manager->get_return_url(), '/wp-admin/' ) + false !== strpos( parse_url( wp_get_referer(), PHP_URL_QUERY ), 'customize_changeset_uuid=' . $wp_customize->changeset_uuid() ) ); if ( $should_add_snapshot_uuid ) { + $wp_customize = $this->get_customize_manager(); $args_name = $this->get_front_uuid_param(); $args = array( - $args_name => $this->current_snapshot_uuid, + $args_name => $wp_customize->changeset_uuid(), ); - $return_url = add_query_arg( array_map( 'rawurlencode', $args ), $this->customize_manager->get_return_url() ); - $this->customize_manager->set_return_url( $return_url ); + $return_url = add_query_arg( array_map( 'rawurlencode', $args ), $wp_customize->get_return_url() ); + $this->get_customize_manager()->set_return_url( $return_url ); } } @@ -282,20 +238,19 @@ static public function encode_json( $value ) { * @global \WP_Customize_Manager $wp_customize */ public function enqueue_controls_scripts() { - $this->ensure_customize_manager(); - wp_enqueue_style( 'customize-snapshots' ); wp_enqueue_script( 'customize-snapshots' ); $post = null; - if ( $this->snapshot ) { - $post_id = $this->customize_manager->changeset_post_id(); + $preview_url_query_vars = array(); + $post_id = $this->get_customize_manager()->changeset_post_id(); + if ( $post_id ) { $post = get_post( $post_id ); $preview_url_query_vars = $this->post_type->get_customizer_state_query_vars( $post->ID ); if ( $post instanceof \WP_Post ) { $this->override_post_date_default_data( $post ); - $edit_link = $this->snapshot->get_edit_link( $post ); + $edit_link = $this->get_edit_link( $post_id ); } } @@ -362,14 +317,14 @@ public function enqueue_admin_scripts( $hook ) { * Enqueue Customizer frontend scripts. */ public function enqueue_frontend_scripts() { - if ( ! current_user_can( 'customize' ) ) { + if ( ! is_customize_preview() || ! current_user_can( 'customize' ) ) { return; } $handle = 'customize-snapshots-frontend'; wp_enqueue_script( $handle ); $exports = array( - 'uuid' => $this->snapshot ? $this->snapshot->uuid() : null, + 'uuid' => $this->get_customize_manager()->changeset_uuid(), 'home_url' => wp_parse_url( home_url( '/' ) ), 'l10n' => array( 'restoreSessionPrompt' => __( 'It seems you may have inadvertently navigated away from previewing a customized state. Would you like to restore the changeset context?', 'customize-snapshots' ), @@ -383,15 +338,6 @@ public function enqueue_frontend_scripts() { ); } - /** - * Get the Customize_Snapshot instance. - * - * @return Customize_Snapshot - */ - public function snapshot() { - return $this->snapshot; - } - /** * Create initial changeset revision. * @@ -559,7 +505,7 @@ public function print_admin_bar_styles() { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function replace_customize_link( $wp_admin_bar ) { - if ( empty( $this->snapshot ) ) { + if ( ! is_customize_preview() ) { return; } @@ -581,14 +527,15 @@ public function replace_customize_link( $wp_admin_bar ) { ); } + $wp_customize = $this->get_customize_manager(); $args = array( - $this->get_customize_uuid_param() => $this->current_snapshot_uuid, + $this->get_customize_uuid_param() => $wp_customize->changeset_uuid(), ); - $post = $this->snapshot->post(); + $post_id = $wp_customize->changeset_post_id(); - if ( $post ) { - $customizer_state_query_vars = $this->post_type->get_customizer_state_query_vars( $post->ID ); + if ( $post_id ) { + $customizer_state_query_vars = $this->post_type->get_customizer_state_query_vars( $post_id ); unset( $customizer_state_query_vars['url'] ); $args = array_merge( $args, $customizer_state_query_vars ); } @@ -642,17 +589,17 @@ public function add_resume_snapshot_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_post_edit_screen_link( $wp_admin_bar ) { - if ( ! $this->snapshot || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->edit_posts ) ) { + if ( ! is_customize_preview() || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->edit_posts ) ) { return; } - $post = $this->snapshot->post(); - if ( ! $post ) { + $post_id = $this->get_customize_manager()->changeset_post_id(); + if ( ! $post_id ) { return; } $wp_admin_bar->add_menu( array( 'id' => 'inspect-customize-snapshot', 'title' => __( 'Inspect Changeset', 'customize-snapshots' ), - 'href' => $this->snapshot->get_edit_link( $post ), + 'href' => $this->get_edit_link( $post_id ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', ), @@ -665,11 +612,13 @@ public function add_post_edit_screen_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_publish_changeset_link( $wp_admin_bar ) { - if ( ! $this->snapshot || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) ) { + if ( ! is_customize_preview() || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) ) { return; } - $post = $this->snapshot->post(); - if ( ! $post ) { + + $wp_customize = $this->get_customize_manager(); + $post_id = $wp_customize->changeset_post_id(); + if ( ! $post_id ) { return; } @@ -677,7 +626,7 @@ public function add_publish_changeset_link( $wp_admin_bar ) { array( 'post_type' => $this->post_type->get_slug(), 'action' => 'frontend_publish', - 'uuid' => $this->current_snapshot_uuid, + 'uuid' => $wp_customize->changeset_uuid(), 'stylesheet' => get_stylesheet(), ), admin_url( 'edit.php' ) @@ -685,7 +634,7 @@ public function add_publish_changeset_link( $wp_admin_bar ) { $wp_admin_bar->add_menu( array( 'id' => 'publish-customize-changeset', 'title' => __( 'Publish Changeset', 'customize-snapshots' ), - 'href' => wp_nonce_url( $href, 'publish-changeset_' . $this->current_snapshot_uuid ), + 'href' => wp_nonce_url( $href, 'publish-changeset_' . $wp_customize->changeset_uuid() ), 'meta' => array( 'class' => 'ab-item ab-customize-snapshots-item', ), @@ -698,7 +647,7 @@ public function add_publish_changeset_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. */ public function add_snapshot_exit_link( $wp_admin_bar ) { - if ( ! $this->snapshot ) { + if ( ! is_customize_preview() ) { return; } $wp_admin_bar->add_menu( array( @@ -1036,7 +985,7 @@ public function override_post_date_default_data( \WP_Post &$post ) { * @return string Post type. */ public function get_post_type() { - return constant( get_class( $this->post_type ) . '::SLUG' ); + return Post_Type::SLUG; } /** @@ -1045,7 +994,7 @@ public function get_post_type() { * @return string param. */ public function get_front_uuid_param() { - return constant( get_class( $this->post_type ) . '::FRONT_UUID_PARAM_NAME' ); + return Post_Type::FRONT_UUID_PARAM_NAME; } /** @@ -1054,13 +1003,13 @@ public function get_front_uuid_param() { * @return string customize param name. */ public function get_customize_uuid_param() { - return constant( get_class( $this->post_type ) . '::CUSTOMIZE_UUID_PARAM_NAME' ); + return Post_Type::CUSTOMIZE_UUID_PARAM_NAME; } /** * Handles request to publish changeset from frontend. */ - public function handle_frontend_changset_publish() { + public function handle_frontend_changeset_publish() { if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'frontend_publish' !== $_GET['action'] ) { return; @@ -1069,10 +1018,9 @@ public function handle_frontend_changset_publish() { if ( ! static::is_valid_uuid( $uuid ) ) { return; } - $this->current_snapshot_uuid = $uuid; $is_user_authorized = ( - check_ajax_referer( 'publish-changeset_' . $this->current_snapshot_uuid, false, false ) + check_ajax_referer( 'publish-changeset_' . $uuid, false, false ) && current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) ); @@ -1087,6 +1035,7 @@ public function handle_frontend_changset_publish() { ); } + $stylesheet = null; if ( isset( $_GET['stylesheet'] ) ) { $theme = wp_get_theme( wp_unslash( $_GET['stylesheet'] ) ); if ( $theme->errors() ) { @@ -1101,11 +1050,20 @@ public function handle_frontend_changset_publish() { ) ); } - $this->stylesheet = $theme->get_stylesheet(); + $stylesheet = $theme->get_stylesheet(); } - $this->ensure_customize_manager(); - $r = $this->customize_manager->save_changeset_post( array( + $wp_customize = $this->get_customize_manager(); + $args = array(); + if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { + require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); + $args['changeset_uuid'] = $uuid; + if ( $stylesheet ) { + $args['theme'] = $stylesheet; + } + $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. + } + $r = $wp_customize->save_changeset_post( array( 'status' => 'publish', ) ); diff --git a/php/class-customize-snapshot.php b/php/class-customize-snapshot.php deleted file mode 100644 index 357607e7..00000000 --- a/php/class-customize-snapshot.php +++ /dev/null @@ -1,83 +0,0 @@ -snapshot_manager = $snapshot_manager; - } - - /** - * Get the snapshot post associated with the provided UUID, or null if it does not exist. - * - * @return \WP_Post|null Post or null. - */ - public function post() { - $post_id = $this->snapshot_manager->customize_manager->changeset_post_id(); - if ( $post_id ) { - return get_post( $post_id ); - } - return null; - } - - /** - * Get the snapshot uuid. - * - * @return string - */ - public function uuid() { - return $this->snapshot_manager->customize_manager->changeset_uuid(); - } - - /** - * Get edit post link. - * - * @param int|\WP_Post $post_id Post. - * - * @return null|string Post edit link. - */ - public function get_edit_link( $post_id ) { - $has_filter = has_filter( 'get_edit_post_link', '__return_empty_string' ); - if ( $has_filter ) { - remove_filter( 'get_edit_post_link', '__return_empty_string' ); - } - $link = get_edit_post_link( $post_id, 'raw' ); - if ( $has_filter ) { - add_filter( 'get_edit_post_link', '__return_empty_string' ); - } - return $link; - } - -} diff --git a/php/class-migrate.php b/php/class-migrate.php deleted file mode 100644 index 625c5fcc..00000000 --- a/php/class-migrate.php +++ /dev/null @@ -1,244 +0,0 @@ -plugin = $plugin; - if ( ! $plugin->compat && is_admin() && is_super_admin() ) { - $this->maybe_migrate(); - } - } - - /** - * Is already migrated or not. - * - * @return bool status of migration. - */ - public function is_migrated() { - $snapshot_migrate_option = get_option( self::KEY ); - return ! empty( $snapshot_migrate_option ); - } - - /** - * Migrate if wp version is 4.7 and above. - */ - public function maybe_migrate() { - if ( ! $this->is_migrated() ) { - $found_post = $this->changeset_migrate( 1, true ); - if ( empty( $found_post ) ) { - update_option( self::KEY, 1 ); - return; - } - add_action( 'admin_notices', array( $this, 'show_migration_notice' ) ); - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_script' ) ); - add_action( 'wp_ajax_customize_snapshot_migration', array( $this, 'handle_migrate_changeset_request' ) ); - } - } - - /** - * Migrate 20 posts at a time. - */ - public function handle_migrate_changeset_request() { - check_ajax_referer( 'customize-snapshot-migration', 'nonce' ); - $limit = isset( $_REQUEST['limit'] ) ? absint( $_REQUEST['limit'] ) : 20; // WPCS: input var ok. - $found_posts = $this->changeset_migrate( $limit ); - $remaining_post = ( $found_posts < $limit ) ? 0 : $found_posts - $limit; - $data = array( - 'remaining_posts' => $remaining_post, - ); - if ( ! $remaining_post ) { - update_option( self::KEY, 1 ); - } - wp_send_json_success( $data ); - } - - /** - * Print migration javascript script. - */ - public function enqueue_script() { - wp_enqueue_script( 'customize-snapshot-migrate' ); - } - - /** - * Show admin notice to migrate. - */ - public function show_migration_notice() { - ?> -
-

- %s %s ', esc_html__( 'Click', 'customize-snapshots' ), esc_html__( 'Migration of snapshots to changesets complete!', 'customize-snapshots' ), esc_html__( 'here', 'customize-snapshots' ), esc_html__( 'to start migration.', 'customize-snapshots' ) ); - ?> -

-
- 'customize_snapshot', - 'no_found_rows' => false, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'post_status' => array_keys( get_post_stati() ), - 'posts_per_page' => $limit, - 'fields' => 'ids', // We will use get_post() to fetch each posts. - ); - - if ( -1 === $limit ) { - $arg['no_found_rows'] = true; - } - - $query->query( $arg ); - if ( $dry_run ) { - return $query->posts; - } - - if ( $is_doing_cli ) { - /* translators: %s: post count.*/ - \WP_CLI::log( sprintf( __( 'Migrating %s Snapshots into Changesets', 'customize-snapshots' ), count( $query->posts ) ) ); - } - - if ( ! empty( $query->posts ) ) { - $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); - if ( $has_kses ) { - kses_remove_filters(); // Prevent KSES from corrupting JSON in post_content. - } - if ( ! class_exists( '\WP_Customize_Manager' ) ) { - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - } - foreach ( $query->posts as $id ) { - $success = $this->migrate_post( $id ); - if ( $is_doing_cli ) { - if ( $success ) { - /* translators: %s: post id.*/ - \WP_CLI::success( sprintf( __( 'Migrated post %s.', 'customize-snapshots' ), $id ) ); - } else { - /* translators: %s: post id.*/ - \WP_CLI::error( sprintf( __( 'Failed to migrate %s.', 'customize-snapshots' ), $id ) ); - } - } - } - if ( $has_kses ) { - kses_init_filters(); - } - } - if ( -1 === $limit ) { - update_option( self::KEY, 1 ); - return count( $query->posts ); - } else { - return $query->found_posts; - } - } - - /** - * Migrate a post. - * - * @param int $id Post ID. - * @return int|\WP_Error maybe updated. - * @global \WP_Customize_Manager $wp_customize - */ - public function migrate_post( $id ) { - global $wp_customize, $wpdb; - - $post = get_post( $id ); - - // Get data. - $data = json_decode( $post->post_content, true ); - if ( json_last_error() || ! is_array( $data ) ) { - $data = array(); - } - - // Get manager instance. - $manager = new \WP_Customize_Manager(); - $original_manager = $wp_customize; - $wp_customize = $manager; // Export to global since some filters (like widget_customizer_setting_args) lack as $wp_customize context and need global. WPCS: override ok. - - // Validate data. - foreach ( $data as $setting_id => $setting_params ) { - // Amend post values with any supplied data. - if ( array_key_exists( 'value', $setting_params ) ) { - $manager->set_post_value( $setting_id, $setting_params['value'] ); // Add to post values so that they can be validated and sanitized. - } - } - do_action( 'customize_register', $manager ); - - // Note that in addition to post data, this will include any stashed theme mods. - $post_values = $manager->unsanitized_post_values( array( - 'exclude_changeset' => true, - 'exclude_post_data' => false, - ) ); - - // Update data as new changeset. - $manager->add_dynamic_settings( array_keys( $post_values ) ); - $theme = get_post_meta( $id, '_snapshot_theme', true ); - $post_data = array(); - foreach ( $post_values as $setting_id => $setting_value ) { - $setting = $manager->get_setting( $setting_id ); - - if ( $setting && 'theme_mod' === $setting->type ) { - $prefixed_setting_id = $theme . '::' . $setting->id; - } else { - $prefixed_setting_id = $setting_id; - } - $post_data[ $prefixed_setting_id ] = array( - 'value' => $setting_value, - 'user_id' => $post->post_author, - ); - if ( $setting instanceof \WP_Customize_Setting ) { - $post_data[ $prefixed_setting_id ]['type'] = $setting->type; - } - } - $maybe_updated = $wpdb->update( $wpdb->posts, - array( - 'post_type' => 'customize_changeset', - 'post_content' => Customize_Snapshot_Manager::encode_json( $post_data ), - ), - array( - 'ID' => $post->ID, - ) - ); // WPCS: DB call ok and cache ok, because doing update query, and using direct DB call to bypass weight of triggered hooks. - clean_post_cache( $post ); - - $wp_customize = $original_manager; // Restore previous manager. WPCS: override ok. - - return $maybe_updated; - } -} diff --git a/php/class-plugin.php b/php/class-plugin.php index 9cc3387f..583c9712 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -17,7 +17,7 @@ class Plugin extends Plugin_Base { * * @todo Rename this to just `$manager` and let the class be `Manager`. * - * @var Customize_Snapshot_Manager|Customize_Snapshot_Manager_Back_Compat + * @var Customize_Snapshot_Manager */ public $customize_snapshot_manager; @@ -28,20 +28,6 @@ class Plugin extends Plugin_Base { */ public $version; - /** - * Is old version of WordPress. - * - * @var boolean - */ - public $compat; - - /** - * Migration handler. - * - * @var Migrate - */ - public $migrate; - /** * Plugin constructor. */ @@ -50,24 +36,11 @@ public function __construct() { if ( preg_match( '/Version:\s*(\S+)/', file_get_contents( __DIR__ . '/../customize-snapshots.php' ), $matches ) ) { // @codingStandardsIgnoreLine because file_get_contents() is not requesting a URL. $this->version = $matches[1]; } - $this->compat = is_back_compat(); load_plugin_textdomain( 'customize-snapshots' ); $this->param_back_compat(); parent::__construct(); } - /** - * Init migration. - * - * @action init - */ - public function init_migration() { - $this->migrate = new Migrate( $this ); - if ( defined( 'WP_CLI' ) && WP_CLI ) { - require_once( __DIR__ . '/class-customize-snapshot-command.php' ); - } - } - /** * Initiate the plugin resources. * @@ -79,11 +52,7 @@ public function init_migration() { * @action after_setup_theme, 8 */ public function init() { - if ( $this->compat ) { - $this->customize_snapshot_manager = new Customize_Snapshot_Manager_Back_Compat( $this ); - } else { - $this->customize_snapshot_manager = new Customize_Snapshot_Manager( $this ); - } + $this->customize_snapshot_manager = new Customize_Snapshot_Manager( $this ); $this->customize_snapshot_manager->init(); } @@ -103,32 +72,10 @@ public function register_scripts( \WP_Scripts $wp_scripts ) { $deps = array( 'jquery', 'jquery-ui-dialog', 'jquery-ui-selectmenu', 'wp-util', 'customize-controls' ); $wp_scripts->add( $handle, $src, $deps ); - if ( $this->compat ) { - $handle = 'customize-snapshots-compat'; - $src = $this->dir_url . 'js/compat/customize-snapshots' . $min . '.js'; - $deps = array( 'customize-snapshots' ); - $wp_scripts->add( $handle, $src, $deps ); - - $handle = 'customize-snapshots-preview'; - $src = $this->dir_url . 'js/compat/customize-snapshots-preview' . $min . '.js'; - $deps = array( 'customize-preview' ); - $wp_scripts->add( $handle, $src, $deps ); - - $handle = 'customize-snapshots-frontend'; - $src = $this->dir_url . 'js/compat/customize-snapshots-frontend' . $min . '.js'; - $deps = array( 'jquery', 'underscore' ); - $wp_scripts->add( $handle, $src, $deps ); - } else { - $handle = 'customize-snapshot-migrate'; - $src = $this->dir_url . 'js/customize-migrate' . $min . '.js'; - $deps = array( 'jquery', 'wp-util' ); - $wp_scripts->add( $handle, $src, $deps ); - - $handle = 'customize-snapshots-frontend'; - $src = $this->dir_url . 'js/customize-snapshots-frontend' . $min . '.js'; - $deps = array( 'jquery', 'underscore' ); - $wp_scripts->add( $handle, $src, $deps ); - } + $handle = 'customize-snapshots-frontend'; + $src = $this->dir_url . 'js/customize-snapshots-frontend' . $min . '.js'; + $deps = array( 'jquery', 'underscore' ); + $wp_scripts->add( $handle, $src, $deps ); $handle = 'customize-snapshots-admin'; $src = $this->dir_url . 'js/customize-snapshots-admin' . $min . '.js'; @@ -171,7 +118,7 @@ public function register_styles( \WP_Styles $wp_styles ) { * Continue allowing support of param customize_snapshot_uuid in 4.7+. */ public function param_back_compat() { - if ( isset( $_REQUEST['customize_snapshot_uuid'] ) && ! $this->compat ) { // WPCS: input var ok. CSRF ok. + if ( isset( $_REQUEST['customize_snapshot_uuid'] ) ) { // WPCS: input var ok. CSRF ok. $_REQUEST['customize_changeset_uuid'] = $_REQUEST['customize_snapshot_uuid']; // WPCS: input var ok. CSRF ok. Sanitization ok. $_GET['customize_changeset_uuid'] = $_REQUEST['customize_snapshot_uuid']; // WPCS: input var ok. CSRF ok. Sanitization ok. $_POST['customize_changeset_uuid'] = $_REQUEST['customize_snapshot_uuid']; // WPCS: input var ok. CSRF ok. Sanitization ok. diff --git a/php/class-post-type-back-compat.php b/php/class-post-type-back-compat.php deleted file mode 100644 index 79a30083..00000000 --- a/php/class-post-type-back-compat.php +++ /dev/null @@ -1,241 +0,0 @@ - _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' => $labels, - 'description' => __( 'Customize Snapshots.', 'customize-snapshots' ), - 'public' => true, - '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, - 'delete_with_user' => false, - 'menu_position' => null, - 'supports' => array( 'title', 'author', 'revisions' ), - 'capability_type' => static::SLUG, - 'capabilities' => array( - 'create_posts' => 'do_not_allow', - ), - 'rewrite' => false, - 'show_in_customizer' => false, // Prevent inception. - 'show_in_rest' => true, - 'rest_base' => 'customize_snapshots', - 'rest_controller_class' => __NAMESPACE__ . '\\Snapshot_REST_API_Controller', - 'customize_snapshot_post_type_obj' => $this, - 'menu_icon' => 'dashicons-camera', - 'register_meta_box_cb' => array( $this, 'setup_metaboxes' ), - ); - - register_post_type( static::SLUG, $args ); - - // Call parent hooks. - $this->hooks(); - - // 4.6.x and post-type specific hooks. - add_action( 'admin_notices', array( $this, 'show_publish_error_admin_notice' ) ); - add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 ); - add_action( 'admin_footer-edit.php', array( $this, 'snapshot_merge_print_script' ) ); - add_action( 'load-edit.php', array( $this, 'handle_snapshot_merge_workaround' ) ); - add_filter( 'post_type_link', array( $this, 'filter_post_type_link' ), 10, 2 ); - add_filter( 'wp_insert_post_data', array( $this, 'preserve_post_name_in_insert_data' ), 10, 2 ); - } - - /** - * Insert script for adding merge snapshot bulk action polyfill. - */ - public function snapshot_merge_print_script() { - global $post_type; - if ( static::SLUG === $post_type ) { - ?> - - current_action(); - if ( 'merge_snapshot' !== $action || ( isset( $_REQUEST['post_type'] ) && static::SLUG !== sanitize_key( wp_unslash( $_REQUEST['post_type'] ) ) ) ) { // WPCS: input var ok. CSRF ok. - return; - } - if ( ! isset( $_REQUEST['post'] ) || ! is_array( $_REQUEST['post'] ) ) { // WPCS: input var ok. CSRF ok. - return; - } - check_admin_referer( 'bulk-posts' ); - $post_ids = array_map( 'intval', $_REQUEST['post'] ); // WPCS: input var ok. CSRF ok. - if ( empty( $post_ids ) ) { - return; - } - $redirect_url = $this->handle_snapshot_merge( wp_get_referer(), 'merge_snapshot', $post_ids ); - if ( ! empty( $redirect_url ) ) { - wp_safe_redirect( $redirect_url ); - exit; - } - } - - /** - * Find a snapshot post by UUID. - * - * @param string $uuid UUID. - * @return int|null Post ID or null if not found. - */ - public function find_post( $uuid ) { - add_action( 'pre_get_posts', array( $this, '_override_wp_query_is_single' ) ); - $query = new \WP_Query( array( - 'name' => $uuid, - 'posts_per_page' => 1, - 'post_type' => static::SLUG, - 'post_status' => get_post_stati(), - 'no_found_rows' => true, - 'ignore_sticky_posts' => true, - 'cache_results' => false, - ) ); - $posts = $query->posts; - remove_action( 'pre_get_posts', array( $this, '_override_wp_query_is_single' ) ); - - $post = array_shift( $posts ); - if ( $post ) { - return $post->ID; - } else { - return null; - } - } - - /** - * Preserve the post_name when submitting a snapshot for review. - * - * @see wp_insert_post() - * @link https://github.com/xwp/wordpress-develop/blob/831a186108983ade4d647124d4e56e09aa254704/src/wp-includes/post.php#L3134-L3137 - * - * @param array $post_data Post data. - * @param array $original_post_data Original post data. - * @return array Post data. - */ - public function preserve_post_name_in_insert_data( $post_data, $original_post_data ) { - if ( empty( $post_data['post_type'] ) || static::SLUG !== $post_data['post_type'] ) { - return $post_data; - } - if ( empty( $post_data['post_name'] ) && 'pending' === $post_data['post_status'] ) { - $post_data['post_name'] = $original_post_data['post_name']; - } - return $post_data; - } - - /** - * Display snapshot save error on post list table. - * - * @param array $states Display states. - * @param \WP_Post $post Post object. - * - * @return mixed - */ - public function display_post_states( $states, $post ) { - if ( static::SLUG !== $post->post_type ) { - return $states; - } - $maybe_error = get_post_meta( $post->ID, 'snapshot_error_on_publish', true ); - if ( $maybe_error ) { - $states['snapshot_error'] = __( 'Error on publish', 'customize-snapshots' ); - } - return $states; - } - - /** - * Show an admin notice when publishing fails and the post gets kicked back to pending. - */ - public function show_publish_error_admin_notice() { - if ( ! function_exists( 'get_current_screen' ) ) { - return; - } - $current_screen = get_current_screen(); - if ( ! $current_screen || static::SLUG !== $current_screen->id || 'post' !== $current_screen->base ) { - return; - } - if ( ! isset( $_REQUEST['snapshot_error_on_publish'] ) ) { // WPCS: input var ok. CSRF ok. - return; - } - ?> -
-

-
- snapshot_manager->ensure_customize_manager(); - return $this->snapshot_manager->customize_manager->find_changeset_post_id( $uuid ); + $manager = $this->snapshot_manager->ensure_customize_manager(); + return $manager->find_changeset_post_id( $uuid ); } /** @@ -974,7 +974,7 @@ public function set_customizer_state_query_vars( $post_id, $query_vars ) { $stored_query_vars = array(); $autofocus_query_vars = array( 'autofocus[panel]', 'autofocus[section]', 'autofocus[control]' ); - $this->snapshot_manager->ensure_customize_manager(); + $wp_customize = $this->snapshot_manager->ensure_customize_manager(); foreach ( wp_array_slice_assoc( $query_vars, $autofocus_query_vars ) as $key => $value ) { if ( preg_match( '/^[a-z|\[|\]|_|\-|0-9]+$/', $value ) ) { @@ -984,14 +984,14 @@ public function set_customizer_state_query_vars( $post_id, $query_vars ) { if ( ! empty( $query_vars['url'] ) && wp_validate_redirect( $query_vars['url'] ) ) { $stored_query_vars['url'] = esc_url_raw( $query_vars['url'] ); } - if ( isset( $query_vars['device'] ) && in_array( $query_vars['device'], array_keys( $this->snapshot_manager->customize_manager->get_previewable_devices() ), true ) ) { + if ( isset( $query_vars['device'] ) && in_array( $query_vars['device'], array_keys( $wp_customize->get_previewable_devices() ), true ) ) { $stored_query_vars['device'] = $query_vars['device']; } if ( isset( $query_vars['scroll'] ) && is_int( $query_vars['scroll'] ) ) { $stored_query_vars['scroll'] = $query_vars['scroll']; } if ( isset( $query_vars['previewing_theme'] ) ) { - $theme = $this->snapshot_manager->customize_manager->get_stylesheet(); + $theme = $wp_customize->get_stylesheet(); $stored_query_vars['theme'] = $query_vars['previewing_theme'] ? $theme : ''; } update_post_meta( $post_id, '_preview_url_query_vars', $stored_query_vars ); diff --git a/readme.txt b/readme.txt index 121e9e64..66a06a4f 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Customize Snapshots === Contributors: xwp, westonruter, valendesigns, utkarshpatel, sayedwp, newscorpau -Requires at least: 4.6 +Requires at least: 4.7 Tested up to: 4.8.1 Stable tag: 0.6.2 Requires PHP: 5.3 From 2e02ccd8a6e75030b4d9ddc87070c0056b06fec3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 21 Oct 2017 00:41:32 -0700 Subject: [PATCH 34/90] Update unit tests, remove more obsolete code, update clean_up_nav_menus_created_auto_drafts() for 4.9 --- .travis.yml | 2 +- instance.php | 15 +- php/class-customize-snapshot-manager.php | 16 +- ...customize-snapshot-manager-back-compat.php | 626 --------------- ...t-class-customize-snapshot-back-compat.php | 363 --------- ...customize-snapshot-manager-back-compat.php | 746 ------------------ .../test-class-customize-snapshot-manager.php | 207 ++--- tests/php/test-class-customize-snapshot.php | 155 ---- tests/php/test-class-migrate.php | 263 ------ .../php/test-class-post-type-back-compat.php | 295 ------- tests/php/test-class-post-type.php | 38 +- tests/php/test-class-snapshot-ajax.php | 83 -- ...est-class-snapshot-rest-api-controller.php | 6 +- tests/test-customize-snapshots.php | 31 +- 14 files changed, 70 insertions(+), 2776 deletions(-) delete mode 100644 tests/php/test-class-ajax-customize-snapshot-manager-back-compat.php delete mode 100644 tests/php/test-class-customize-snapshot-back-compat.php delete mode 100644 tests/php/test-class-customize-snapshot-manager-back-compat.php delete mode 100644 tests/php/test-class-customize-snapshot.php delete mode 100644 tests/php/test-class-migrate.php delete mode 100644 tests/php/test-class-post-type-back-compat.php delete mode 100644 tests/php/test-class-snapshot-ajax.php diff --git a/.travis.yml b/.travis.yml index 9a461a9d..8a3ccbd7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ php: env: - WP_VERSION=trunk WP_MULTISITE=0 - WP_VERSION=latest WP_MULTISITE=0 - - WP_VERSION=4.6.1 WP_MULTISITE=0 + - WP_VERSION=4.7.6 WP_MULTISITE=0 - WP_VERSION=latest WP_MULTISITE=1 install: diff --git a/instance.php b/instance.php index 350fa45c..99a86952 100644 --- a/instance.php +++ b/instance.php @@ -34,11 +34,7 @@ function get_plugin_instance() { */ function is_previewing_settings() { $manager = get_plugin_instance()->customize_snapshot_manager; - if ( get_plugin_instance()->compat ) { - return $manager->is_previewing_settings(); - } else { - return ( isset( $manager->customize_manager ) && $manager->customize_manager->is_preview() ) || did_action( 'customize_preview_init' ); - } + return ( isset( $manager->customize_manager ) && $manager->customize_manager->is_preview() ) || did_action( 'customize_preview_init' ); } /** @@ -47,13 +43,14 @@ function is_previewing_settings() { * @see Customize_Snapshot_Manager::$current_snapshot_uuid * * @return string|null The current snapshot UUID or null if no snapshot. + * @global \WP_Customize_Manager $wp_customize */ function current_snapshot_uuid() { - $customize_snapshot_uuid = get_plugin_instance()->customize_snapshot_manager->current_snapshot_uuid; - if ( empty( $customize_snapshot_uuid ) ) { + global $wp_customize; + if ( empty( $wp_customize ) ) { return null; } else { - return $customize_snapshot_uuid; + return $wp_customize->changeset_uuid(); } } @@ -70,5 +67,5 @@ function is_back_compat() { if ( false !== $pos ) { $wp_version = substr( $wp_version, 0, $pos ); } - return version_compare( $wp_version, '4.7', '<' ); + return version_compare( $wp_version, '4.9', '<' ); } diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index f3299b56..dc8422c3 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -44,14 +44,6 @@ class Customize_Snapshot_Manager { */ protected $previewing_settings = false; - /** - * The originally active theme. - * - * @access public - * @var string - */ - public $original_stylesheet; - /** * New active theme. * @@ -69,7 +61,6 @@ class Customize_Snapshot_Manager { */ public function __construct( Plugin $plugin ) { $this->plugin = $plugin; - $this->original_stylesheet = get_stylesheet(); } /** @@ -97,6 +88,7 @@ function hooks() { add_filter( 'wp_insert_post_data', array( $this, 'prepare_snapshot_post_content_for_publish' ) ); remove_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' ); add_action( 'delete_post', array( $this, 'clean_up_nav_menus_created_auto_drafts' ) ); + add_filter( 'customize_save_response', array( $this, 'add_snapshot_var_to_customize_save' ), 10, 2 ); } /** @@ -105,7 +97,6 @@ function hooks() { function init() { $this->post_type = new Post_Type( $this ); $this->hooks(); - add_filter( 'customize_save_response', array( $this, 'add_snapshot_var_to_customize_save' ), 10, 2 ); } /** @@ -204,7 +195,6 @@ public function add_snapshot_uuid_to_return_url() { false !== strpos( parse_url( wp_get_referer(), PHP_URL_QUERY ), 'customize_changeset_uuid=' . $wp_customize->changeset_uuid() ) ); if ( $should_add_snapshot_uuid ) { - $wp_customize = $this->get_customize_manager(); $args_name = $this->get_front_uuid_param(); $args = array( $args_name => $wp_customize->changeset_uuid(), @@ -666,7 +656,7 @@ public function add_snapshot_exit_link( $wp_admin_bar ) { * @param \WP_Admin_Bar $wp_admin_bar Admin bar. */ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { - if ( empty( $this->snapshot ) || ! current_user_can( 'customize' ) ) { + if ( ! is_customize_preview() || ! current_user_can( 'customize' ) ) { return; } $snapshot_admin_bar_node_ids = array( @@ -1135,7 +1125,7 @@ public function clean_up_nav_menus_created_auto_drafts( $changeset_post_id ) { } remove_action( 'delete_post', array( $this, 'clean_up_nav_menus_created_auto_drafts' ) ); foreach ( $data['nav_menus_created_posts']['value'] as $nav_menu_created_post_id ) { - if ( 'auto-draft' !== get_post_status( $nav_menu_created_post_id ) ) { + if ( 'auto-draft' !== get_post_status( $nav_menu_created_post_id ) && 'draft' !== get_post_status( $nav_menu_created_post_id ) ) { continue; } diff --git a/tests/php/test-class-ajax-customize-snapshot-manager-back-compat.php b/tests/php/test-class-ajax-customize-snapshot-manager-back-compat.php deleted file mode 100644 index d33c9957..00000000 --- a/tests/php/test-class-ajax-customize-snapshot-manager-back-compat.php +++ /dev/null @@ -1,626 +0,0 @@ -compat ) { - $this->markTestSkipped( 'WordPress Version 4.6.x or below is required for this test-case.' ); - } - - remove_all_actions( 'wp_ajax_customize_save' ); - remove_all_actions( 'wp_ajax_customize_update_snapshot' ); - $this->plugin = new Plugin(); - $this->set_input_vars(); - $this->plugin->init(); - if ( $this->plugin->compat ) { - $this->post_type_slug = Post_Type_Back_Compat::SLUG; - } else { - $this->post_type_slug = Post_Type::SLUG; - } - } - - /** - * Grant Customize to all. - * - * @param array $allcaps All caps. - * @param array $caps Caps. - * @param array $args Args. - * @return array Caps. - */ - public function filter_grant_customize_to_all( $allcaps, $caps, $args ) { - if ( ! empty( $args ) && 'customize' === $args[0] ) { - $allcaps = array_merge( $allcaps, array_fill_keys( $caps, true ) ); - } - return $allcaps; - } - - /** - * Set input vars. - * - * @param array $vars Input vars. - * @param string $method Request method. - */ - public function set_input_vars( array $vars = array(), $method = 'POST' ) { - $vars = array_merge( - array( - 'customized' => wp_json_encode( array( 'anyonecanedit' => 'Hello' ) ), - 'wp_customize' => 'on', - 'customize_snapshot_uuid' => self::UUID, - 'nonce' => wp_create_nonce( 'save-customize_' . get_stylesheet() ), - ), - $vars - ); - $_GET = $_POST = $_REQUEST = wp_slash( $vars ); - $_SERVER['REQUEST_METHOD'] = $method; - } - - /** - * Set current user. - * - * @param string $role Role. - * @return int User Id. - */ - function set_current_user( $role ) { - $user_id = $this->factory()->user->create( array( 'role' => $role ) ); - wp_set_current_user( $user_id ); - $_GET['nonce'] = $_REQUEST['nonce'] = $_POST['nonce'] = wp_create_nonce( 'save-customize_' . get_stylesheet() ); - return $user_id; - } - - /** - * Add anyonecanedit Customize setting. - */ - function add_setting() { - $this->plugin->customize_snapshot_manager->customize_manager->add_setting( 'anyonecanedit', array( - 'capability' => 'exist', - ) ); - } - - /** - * Tear down. - */ - function tearDown() { - $this->plugin->customize_snapshot_manager->customize_manager = null; - $this->manager = null; - $this->actioned_snapshot = null; - $this->actioned_snapshot_manager = null; - $this->filtered_customizer = null; - unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['wp_scripts'] ); - unset( $_SERVER['REQUEST_METHOD'] ); - unset( $_REQUEST['wp_customize'] ); - unset( $_REQUEST['customize_snapshot_uuid'] ); - unset( $_REQUEST['preview'] ); - parent::tearDown(); - } - - /** - * Helper to keep it DRY - * - * @param string $action Action. - */ - protected function make_ajax_call( $action ) { - try { - $this->_handleAjax( $action ); - } catch ( \WPAjaxDieContinueException $e ) { - unset( $e ); - } - } - - /** - * Testing passing Customize save for a user who has customize_publish capability. - */ - function test_ajax_customize_save_passing_customize_publish() { - $this->set_current_user( 'administrator' ); - $this->plugin->customize_snapshot_manager->customize_manager->setup_theme(); - $this->add_setting(); - - $snapshot_uuid = $this->plugin->customize_snapshot_manager->current_snapshot_uuid; - $snapshot_post_id = $this->plugin->customize_snapshot_manager->post_type->find_post( $snapshot_uuid ); - $this->assertNull( $snapshot_post_id ); - - // Get the results. - $this->make_ajax_call( 'customize_save' ); - $response = json_decode( $this->_last_response, true ); - - $this->assertTrue( $response['success'] ); - if ( method_exists( 'WP_Customize_Manager', 'prepare_setting_validity_for_js' ) ) { - $this->assertArrayHasKey( 'setting_validities', $response['data'] ); - $this->assertArrayHasKey( 'anyonecanedit', $response['data']['setting_validities'] ); - $this->assertTrue( $response['data']['setting_validities']['anyonecanedit'] ); - } - $this->assertArrayHasKey( 'new_customize_snapshot_uuid', $response['data'] ); - $this->assertTrue( Customize_Snapshot_Manager::is_valid_uuid( $response['data']['new_customize_snapshot_uuid'] ) ); - - $snapshot_post_id = $this->plugin->customize_snapshot_manager->post_type->find_post( $snapshot_uuid ); - $this->assertNotNull( $snapshot_post_id ); - $snapshot_post = get_post( $snapshot_post_id ); - $this->assertSame( - $this->plugin->customize_snapshot_manager->customize_manager->unsanitized_post_values(), - wp_list_pluck( json_decode( $snapshot_post->post_content, true ), 'value' ) - ); - } - - /** - * Testing failing a user who lacks customize_publish capability. - */ - function test_ajax_customize_save_failing_customize_publish() { - - add_filter( 'user_has_cap', array( $this, 'filter_grant_customize_to_all' ), 10, 4 ); - $this->set_current_user( 'editor' ); - $this->plugin->customize_snapshot_manager->customize_manager->setup_theme(); - - // Get the results. - $this->make_ajax_call( 'customize_save' ); - $response = json_decode( $this->_last_response, true ); - $expected_results = array( - 'success' => false, - 'data' => array( - 'error' => 'customize_publish_unauthorized', - ), - ); - - $this->assertSame( $expected_results, $response ); - } - - /** - * Testing capabilities check for the update_snapshot method. - */ - function test_ajax_update_snapshot_nonce_check() { - $this->set_current_user( 'administrator' ); - $this->set_input_vars( array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => 'bad-nonce-12345', - ) ); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - - // Get the results. - $response = json_decode( $this->_last_response, true ); - $expected_results = array( - 'success' => false, - 'data' => 'bad_nonce', - ); - - $this->assertSame( $expected_results, $response ); - } - - /** - * Testing REQUEST_METHOD for the update_snapshot method. - */ - function test_ajax_update_snapshot_post_check() { - $this->set_current_user( 'administrator' ); - $this->set_input_vars( - array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - ), - 'GET' - ); - $this->plugin->customize_snapshot_manager->customize_manager->setup_theme(); - $this->add_setting(); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - - // Get the results. - $response = json_decode( $this->_last_response, true ); - $expected_results = array( - 'success' => false, - 'data' => 'bad_method', - ); - - $this->assertSame( $expected_results, $response ); - } - - /** - * Testing capabilities check for the update_snapshot method - * - * @dataProvider data_update_snapshot_cap_check - * - * @param string $role The role we're checking caps against. - * @param array $expected_results Expected results. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager_Back_Compat::check_customize_publish_authorization() - */ - function test_ajax_update_snapshot_cap_check( $role, $expected_results ) { - $this->set_current_user( $role ); - $this->set_input_vars( - array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - ) - ); - $this->add_setting(); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - - // Get the results. - $response = json_decode( $this->_last_response, true ); - - if ( $response['success'] ) { - $this->assertNotEmpty( $response['data']['edit_link'] ); - $this->assertNotEmpty( $response['data']['snapshot_publish_date'] ); - $this->assertNotEmpty( $response['data']['title'] ); - unset( $response['data']['edit_link'] ); - unset( $response['data']['snapshot_publish_date'] ); - unset( $response['data']['title'] ); - } - $this->assertSame( $expected_results, $response ); - } - - /** - * Data provider for test_ajax_update_snapshot_cap_check(). - * - * Provides various post_args to induce error messages that can be - * compared to the expected_results. - * - * @return array { - * @type array { - * @string string $role The role that will test caps for. - * @array array $expected_results The expected results from the ajax call. - * } - * } - */ - function data_update_snapshot_cap_check() { - $data = array( - array( - 'subscriber', - array( - 'success' => false, - 'data' => 'customize_not_allowed', - ), - ), - array( - 'contributor', - array( - 'success' => false, - 'data' => 'customize_not_allowed', - ), - ), - array( - 'author', - array( - 'success' => false, - 'data' => 'customize_not_allowed', - ), - ), - array( - 'editor', - array( - 'success' => false, - 'data' => 'customize_not_allowed', - ), - ), - ); - - $success_data = array( - 'administrator', - array( - 'success' => true, - 'data' => array( - 'errors' => null, - 'setting_validities' => array( - 'anyonecanedit' => true, - ), - ), - ), - ); - - require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; - if ( ! method_exists( 'WP_Customize_Manager', 'prepare_setting_validity_for_js' ) ) { - $success_data[1]['data'] = array( 'errors' => null ); - } - $data[] = $success_data; - - return $data; - } - - /** - * Testing post_data for the update_snapshot method - */ - function test_ajax_update_snapshot_post_data_check() { - unset( $GLOBALS['wp_customize'] ); - remove_all_actions( 'wp_ajax_' . Customize_Snapshot_Manager::AJAX_ACTION ); - - $this->set_current_user( 'administrator' ); - $this->set_input_vars( array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - 'customize_snapshot_uuid' => self::UUID, - 'customized' => null, - ) ); - - $this->plugin = new Plugin(); - $this->plugin->init(); - $this->add_setting(); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - - // Get the results. - $response = json_decode( $this->_last_response, true ); - $expected_results = array( - 'success' => false, - 'data' => 'missing_snapshot_customized', - ); - - $this->assertSame( $expected_results, $response ); - } - - /** - * Testing a successful response for the update_snapshot method - */ - function test_ajax_update_snapshot_success() { - $this->set_current_user( 'administrator' ); - $this->set_input_vars( array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - 'customize_snapshot_uuid' => self::UUID, - ) ); - $this->add_setting(); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - - // Get the results. - $response = json_decode( $this->_last_response, true ); - $this->assertNull( $response['data']['errors'] ); - } - - /** - * Helper function to make the Ajax call directy to `Customize_Snapshot_Manager::save_snapshot`. - * - * @see Customize_Snapshot_Manager::save_snapshot() - */ - function make_save_snapshot_ajax_call() { - try { - ini_set( 'implicit_flush', false ); - ob_start(); - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->publish_snapshot_with_customize_save_after(); - $buffer = ob_get_clean(); - if ( ! empty( $buffer ) ) { - $this->_last_response = $buffer; - } - } catch ( \WPAjaxDieContinueException $e ) { - unset( $e ); - } - } - - /** - * Testing schedule Snapshot - */ - function test_ajax_update_snapshot_schedule() { - unset( $GLOBALS['wp_customize'] ); - remove_all_actions( 'wp_ajax_' . Customize_Snapshot_Manager::AJAX_ACTION ); - - $post_type_obj = get_post_type_object( $this->post_type_slug ); - $setting_key = 'anyonecanedit'; - $tomorrow = date( 'Y-m-d H:i:s', time() + 86400 ); - $this->set_current_user( 'administrator' ); - $this->assertTrue( current_user_can( $post_type_obj->cap->publish_posts ) ); - $title = 'Hello World! \o/'; - $this->set_input_vars( array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'title' => $title, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - 'customize_snapshot_uuid' => self::UUID, - 'customized' => wp_json_encode( array( $setting_key => 'Hello' ) ), - 'status' => 'future', - 'date' => $tomorrow, // Tomorrow. - ) ); - - $this->plugin = new Plugin(); - $this->plugin->init(); - $this->add_setting(); - - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - $post_id = get_plugin_instance()->customize_snapshot_manager->post_type->find_post( self::UUID ); - $expected_results = array( - 'success' => true, - 'data' => array( - 'errors' => null, - 'setting_validities' => array( $setting_key => true ), - 'edit_link' => get_edit_post_link( $post_id, 'raw' ), - 'snapshot_publish_date' => $tomorrow, - 'title' => $title, - ), - ); - require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; - if ( ! method_exists( 'WP_Customize_Manager', 'prepare_setting_validity_for_js' ) ) { - unset( $expected_results['data']['setting_validities'] ); - } - // Get the results. - $response = json_decode( $this->_last_response, true ); - $this->assertSame( $expected_results, $response ); - $this->assertEquals( 'future', get_post_status( $post_id ) ); - $this->assertEquals( $title, get_the_title( $post_id ) ); - } - - /** - * Test updating a snapshot when the user does not have the customize_publish capability. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::handle_update_snapshot_request() - */ - function test_ajax_update_snapshot_ok_for_draft_and_pending_but_not_future() { - unset( $GLOBALS['wp_customize'] ); - remove_all_actions( 'wp_ajax_' . Customize_Snapshot_Manager::AJAX_ACTION ); - - $post_type_obj = get_post_type_object( $this->post_type_slug ); - $setting_key = 'anyonecanedit'; - add_filter( 'user_has_cap', function( $allcaps, $caps, $args ) { - $allcaps['customize'] = true; - if ( ! empty( $allcaps['edit_posts'] ) && ! empty( $args ) && 'customize' === $args[0] ) { - $allcaps = array_merge( $allcaps, array_fill_keys( $caps, true ) ); - } - return $allcaps; - }, 10, 3 ); - $tomorrow = date( 'Y-m-d H:i:s', time() + 86400 ); - $this->set_current_user( 'contributor' ); - $this->assertFalse( current_user_can( $post_type_obj->cap->publish_posts ) ); - $post_vars = array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - 'customize_snapshot_uuid' => self::UUID, - 'customized' => wp_json_encode( array( $setting_key => 'Hello' ) ), - 'publish_date' => $tomorrow, // Tomorrow. - ); - - $this->plugin = new Plugin(); - $this->plugin->init(); - $this->add_setting(); - - // Draft pass. - $post_vars['status'] = 'draft'; - $this->set_input_vars( $post_vars ); - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - $response = json_decode( $this->_last_response, true ); - $this->_last_response = ''; - $this->assertTrue( $response['success'] ); - - // Pending pass. - $post_vars['status'] = 'pending'; - $this->set_input_vars( $post_vars ); - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - $response = json_decode( $this->_last_response, true ); - $this->_last_response = ''; - $this->assertTrue( $response['success'] ); - - // Future fail. - $post_vars['status'] = 'future'; - $this->set_input_vars( $post_vars ); - $this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION ); - $response = json_decode( $this->_last_response, true ); - $expected_results = array( - 'success' => false, - 'data' => 'customize_not_allowed', - ); - $this->assertSame( $expected_results, $response ); - } - - /** - * Test actions and filters to make sure they are passing correct params. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::handle_update_snapshot_request() - */ - function test_handle_update_snapshot_request_actions_and_filters() { - unset( $GLOBALS['wp_customize'] ); - remove_all_actions( 'wp_ajax_' . Customize_Snapshot_Manager::AJAX_ACTION ); - - add_filter( 'user_has_cap', function( $allcaps, $caps, $args ) { - $allcaps['customize'] = true; - if ( ! empty( $allcaps['edit_posts'] ) && ! empty( $args ) && 'customize' === $args[0] ) { - $allcaps = array_merge( $allcaps, array_fill_keys( $caps, true ) ); - } - return $allcaps; - }, 10, 3 ); - $this->set_current_user( 'contributor' ); - $post_vars = array( - 'action' => Customize_Snapshot_Manager::AJAX_ACTION, - 'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ), - 'customize_snapshot_uuid' => self::UUID, - ); - - $this->plugin = new Plugin(); - $this->plugin->init(); - $this->add_setting(); - - $that = $this; // For PHP 5.3. - add_action( 'customize_snapshot_save_before', function( $test_snapshot, $test_snapshot_manager ) use ( $that ) { - $that->actioned_snapshot = $test_snapshot; - $that->actioned_snapshot_manager = $test_snapshot_manager; - }, 10, 2 ); - add_filter( 'customize_save_response', function( $data, $test_customizer ) use ( $that ) { - $that->filtered_customizer = $test_customizer; - return $data; - }, 10, 2 ); - - $this->set_input_vars( $post_vars ); - $this->make_ajax_call( Customize_Snapshot_Manager_Back_Compat::AJAX_ACTION ); - - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->ensure_customize_manager(); - $manager->init(); - - $this->assertEquals( $manager->snapshot(), $this->actioned_snapshot ); - $this->assertEquals( $manager, $this->actioned_snapshot_manager ); - $this->assertEquals( $manager->customize_manager, $this->filtered_customizer ); - } -} diff --git a/tests/php/test-class-customize-snapshot-back-compat.php b/tests/php/test-class-customize-snapshot-back-compat.php deleted file mode 100644 index e0da98ce..00000000 --- a/tests/php/test-class-customize-snapshot-back-compat.php +++ /dev/null @@ -1,363 +0,0 @@ -plugin = get_plugin_instance(); - if ( ! $this->plugin->compat ) { - $this->markTestSkipped( 'WordPress Version 4.6.x or below is required for this test-case.' ); - } - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $GLOBALS['wp_customize'] = new \WP_Customize_Manager(); // WPCS: override ok. - $this->snapshot_manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->wp_customize = $GLOBALS['wp_customize']; - wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); - $this->wp_customize->add_setting( 'foo', array( 'default' => 'foo_default' ) ); - $this->wp_customize->add_setting( 'bar', array( 'default' => 'bar_default' ) ); - $this->foo = $this->wp_customize->get_setting( 'foo' ); - $this->bar = $this->wp_customize->get_setting( 'bar' ); - } - - /** - * Bootstrap the customizer. - */ - public static function setUpBeforeClass() { - $args = array( - 'labels' => array( - 'name' => __( 'Customize Snapshots', 'customize-snapshots' ), - 'singular_name' => __( 'Customize Snapshot', 'customize-snapshots' ), - ), - 'public' => false, - 'capability_type' => 'post', - 'map_meta_cap' => true, - 'hierarchical' => false, - 'rewrite' => false, - 'delete_with_user' => false, - 'supports' => array( 'title', 'author', 'revisions' ), - ); - register_post_type( self::POST_TYPE, $args ); - } - - /** - * Tear down after class. - */ - public static function tearDownAfterClass() { - _unregister_post_type( self::POST_TYPE ); - } - - /** - * Tear down. - */ - function tearDown() { - $this->wp_customize = null; - unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['wp_scripts'] ); - $this->filtered_snapshot = null; - parent::tearDown(); - } - - /** - * Test constructor. - * - * @see Customize_Snapshot::__construct() - */ - function test_construct() { - $manager = $this->snapshot_manager; - $manager->init(); - $data = array( 'foo' => array( 'value' => 'bar' ) ); - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => $data, - ) ); - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - $this->assertEquals( $data, $snapshot->data() ); - } - - /** - * Test UUID. - * - * @see Customize_Snapshot::uuid() - */ - function test_uuid() { - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertEquals( self::UUID, $manager->snapshot()->uuid() ); - } - - /** - * Test bad UUID. - * - * @see Customize_Snapshot::uuid() - */ - function test_uuid_throws_exception() { - try { - new Customize_Snapshot_Back_Compat( $this->snapshot_manager, '1234-invalid-UUID' ); - } catch ( \Exception $e ) { - $this->assertContains( 'You\'ve entered an invalid snapshot UUID.', $e->getMessage() ); - return; - } - $this->fail( 'An expected exception has not been raised.' ); - } - - /** - * Test data. - * - * @see Customize_Snapshot::data() - */ - function test_data() { - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - $manager->snapshot()->set( array( 'foo' => array( 'value' => 'foo_default' ) ) ); - $this->assertNotEmpty( $manager->snapshot()->data() ); - $manager->snapshot()->set( array( 'foo' => array( 'value' => 'foo_custom' ) ) ); - $expected = array( - 'foo' => array( - 'value' => 'foo_custom', - ), - ); - $this->assertEquals( $expected, $manager->snapshot()->data() ); - } - - /** - * Test snapshot settings. - * - * @see Customize_Snapshot::settings() - */ - function test_settings() { - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - $this->assertEmpty( $manager->snapshot()->settings() ); - $manager->snapshot()->set( array( 'foo' => array( 'value' => 'foo_default' ) ) ); - $this->assertNotEmpty( $manager->snapshot()->settings() ); - } - - /** - * Test status. - * - * @see Customize_Snapshot::settings() - */ - function test_status() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - $this->assertNull( $snapshot->status() ); - - $data = array( 'foo' => array( 'value' => 'bar' ) ); - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => $data, - 'status' => 'draft', - ) ); - - $this->assertEquals( 'draft', $snapshot->status() ); - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'status' => 'publish', - ) ); - $this->assertEquals( 'publish', $snapshot->status() ); - } - - /** - * Test set. - * - * @see Customize_Snapshot::set() - */ - function test_set() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - $this->bar->capability = 'do_not_allow'; - add_filter( 'customize_sanitize_foo', 'strtoupper' ); - - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - $result = $snapshot->set( array( - 'foo' => array( 'value' => 'ok' ), - 'bar' => array( 'value' => 'unauthorized' ), - 'baz' => array( 'value' => 'unrecognized' ), - ) ); - - $this->assertArrayHasKey( 'errors', $result ); - $this->assertInstanceOf( 'WP_Error', $result['errors'] ); - $wp_error = $result['errors']; - $this->assertArrayHasKey( 'unauthorized_settings', $wp_error->errors ); - $this->assertArrayHasKey( 'unrecognized_settings', $wp_error->errors ); - - $this->assertArrayHasKey( 'sanitized', $result ); - $this->assertArrayHasKey( 'foo', $result['sanitized'] ); - $this->assertArrayNotHasKey( 'bar', $result['sanitized'] ); - $this->assertArrayNotHasKey( 'baz', $result['sanitized'] ); - $this->assertEquals( 'OK', $result['sanitized']['foo'] ); - - $this->assertArrayHasKey( 'validities', $result ); - $this->assertArrayHasKey( 'foo', $result['validities'] ); - $this->assertTrue( $result['validities']['foo'] ); - - $this->assertEmpty( $snapshot->data() ); - - // Success with populated value. - $result = $snapshot->set( array( 'foo' => array( 'value' => 'ok' ) ) ); - $this->assertNull( $result['errors'] ); - $resultant_data = $snapshot->data(); - $this->assertEquals( 'ok', $resultant_data['foo']['value'] ); - } - - /** - * Test set with varying setting params. - * - * @see Customize_Snapshot::set() - */ - function test_set_with_varying_setting_params() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - - $result = $snapshot->set( array( 'foo' => array( 'value' => 'ok' ) ) ); - $this->assertNull( $result['errors'] ); - $resultant_data = $snapshot->data(); - $this->assertEquals( 'ok', $resultant_data['foo']['value'] ); - - // Check setting a param without a value, ensuring that foo still remains but snapshot is amended. - $result = $snapshot->set( array( 'bar' => array( 'extra' => 'ok' ) ) ); - $this->assertNull( $result['errors'] ); - $resultant_data = $snapshot->data(); - $this->assertEquals( 'ok', $resultant_data['foo']['value'] ); - $this->assertArrayHasKey( 'extra', $resultant_data['bar'] ); - $this->assertNull( $resultant_data['bar']['value'] ); - } - - /** - * Test set with a non-array param. - * - * @see Customize_Snapshot::set() - * @expectedException Exception - */ - function test_set_with_non_array_params() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->ensure_customize_manager(); - $manager->init(); - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - $snapshot->set( array( 'foo' => 'bad' ) ); - } - - /** - * Test saved. - * - * @see Customize_Snapshot::saved() - */ - function test_saved() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - $this->assertFalse( $snapshot->saved() ); - - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( 'foo' => array( 'value' => 'bar' ) ), - ) ); - } - - /** - * Snapshot object passed in customize_snapshot_save filter. - * - * @var Customize_Snapshot - */ - public $filtered_snapshot; - - /** - * Test that the snapshot object is passed as the second filter param. - * - * @see Customize_Snapshot::save() - */ - function test_filter_customize_snapshot_save() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->ensure_customize_manager(); - $manager->init(); - - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - - $that = $this; // For PHP 5.3. - add_filter( 'customize_snapshot_save', function( $data, $test_snapshot ) use ( $that ) { - $that->filtered_snapshot = $test_snapshot; - return $data; - }, 10, 2 ); - - $snapshot->save( array( - 'uuid' => self::UUID, - 'data' => array( 'foo' => array( 'value' => 'bar' ) ), - ) ); - - $this->assertEquals( $snapshot, $this->filtered_snapshot ); - } -} diff --git a/tests/php/test-class-customize-snapshot-manager-back-compat.php b/tests/php/test-class-customize-snapshot-manager-back-compat.php deleted file mode 100644 index 6592c6a7..00000000 --- a/tests/php/test-class-customize-snapshot-manager-back-compat.php +++ /dev/null @@ -1,746 +0,0 @@ -plugin = get_plugin_instance(); - if ( ! $this->plugin->compat ) { - $this->markTestSkipped( 'WordPress Version 4.6.x or below is required for this test-case.' ); - } - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $GLOBALS['wp_customize'] = new \WP_Customize_Manager(); // WPCS: global override ok. - $this->wp_customize = $GLOBALS['wp_customize']; - - $this->wp_customize->add_setting( 'foo', array( 'default' => 'foo_default' ) ); - $this->wp_customize->add_setting( 'bar', array( 'default' => 'bar_default' ) ); - - $this->manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->manager->init(); - $this->user_id = $this->factory()->user->create( array( 'role' => 'administrator' ) ); - - remove_action( 'after_setup_theme', 'twentyfifteen_setup' ); - remove_action( 'after_setup_theme', 'twentysixteen_setup' ); - remove_all_actions( 'send_headers' ); // Prevent X-hacker header in VIP Quickstart. - - // For why these hooks have to be removed, see . - $this->css_concat_init_priority = has_action( 'init', 'css_concat_init' ); - if ( $this->css_concat_init_priority ) { - remove_action( 'init', 'css_concat_init', $this->css_concat_init_priority ); - } - $this->js_concat_init_priority = has_action( 'init', 'js_concat_init' ); - if ( $this->js_concat_init_priority ) { - remove_action( 'init', 'js_concat_init', $this->js_concat_init_priority ); - } - } - - /** - * Clean up global scope. - */ - function clean_up_global_scope() { - unset( $GLOBALS['wp_scripts'] ); - unset( $GLOBALS['wp_styles'] ); - unset( $_REQUEST['customize_snapshot_uuid'] ); - unset( $_REQUEST['wp_customize_preview_ajax'] ); - parent::clean_up_global_scope(); - } - - /** - * Tear down. - */ - function tearDown() { - $this->wp_customize = null; - $this->manager = null; - unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['screen'] ); - $_REQUEST = array(); - parent::tearDown(); - } - - /** - * Set wp_customize query param. - */ - function do_customize_on() { - $_REQUEST['wp_customize'] = 'on'; - } - - /** - * Do Customize boot actions. - * - * @param bool $on Whether to turn on Customizer. - */ - function do_customize_boot_actions( $on = false ) { - $_SERVER['REQUEST_METHOD'] = 'POST'; - do_action( 'setup_theme' ); - $_REQUEST['nonce'] = wp_create_nonce( 'preview-customize_' . $this->wp_customize->theme()->get_stylesheet() ); - do_action( 'after_setup_theme' ); - do_action( 'init' ); - do_action( 'wp_loaded' ); - do_action( 'wp', $GLOBALS['wp'] ); - if ( $on ) { - $this->do_customize_on(); - } - } - - /** - * Tests load_snapshot. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::init() - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::load_snapshot() - */ - public function test_load_snapshot() { - global $wp_actions; - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $this->plugin->customize_snapshot_manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( - 'blogname' => array( 'value' => 'Hello' ), - ), - 'status' => 'draft', - ) ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - unset( $wp_actions['setup_theme'] ); - unset( $wp_actions['wp_loaded'] ); - $manager->init(); - $this->assertNotEmpty( $manager->customize_manager ); - $this->assertNotEmpty( $manager->snapshot ); - - $this->assertEquals( 10, has_action( 'setup_theme', array( $manager, 'import_snapshot_data' ) ) ); - $this->assertEquals( 10, has_action( 'wp_head', 'wp_no_robots' ) ); - $this->assertEquals( 11, has_action( 'wp_loaded', array( $manager, 'preview_snapshot_settings' ) ) ); - } - - /** - * Test constructor with Customizer. - * - * @see Customize_Snapshot_Manager_Back_Compat::__construct() - */ - function test_construct_with_customize() { - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - $this->assertTrue( is_customize_preview() ); - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertEquals( $manager->current_snapshot_uuid, self::UUID ); - $this->assertInstanceOf( 'CustomizeSnapshots\Post_Type_Back_Compat', $manager->post_type ); - $this->assertInstanceOf( 'CustomizeSnapshots\Customize_Snapshot_Back_Compat', $manager->snapshot() ); - $this->assertEquals( 0, has_action( 'init', array( $manager, 'create_post_type' ) ) ); - $this->assertEquals( 10, has_action( 'customize_controls_enqueue_scripts', array( $manager, 'enqueue_controls_scripts' ) ) ); - } - - /** - * Test init. - * - * @see Customize_Snapshot_Manager_Back_Compat::init() - */ - function test_init() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertEquals( 10, has_filter( 'customize_refresh_nonces', array( $manager, 'filter_customize_refresh_nonces' ) ) ); - $this->assertEquals( 10, has_action( 'template_redirect', array( $manager, 'show_theme_switch_error' ) ) ); - $this->assertEquals( 10, has_action( 'customize_save_after', array( $manager, 'publish_snapshot_with_customize_save_after' ) ) ); - $this->assertEquals( 10, has_action( 'transition_post_status', array( $manager, 'save_settings_with_publish_snapshot' ) ) ); - $this->assertEquals( 10, has_action( 'wp_ajax_customize_update_snapshot', array( $manager, 'handle_update_snapshot_request' ) ) ); - $this->assertEquals( 10, has_action( 'customize_preview_init', array( $manager, 'customize_preview_init' ) ) ); - $this->assertEquals( 10, has_action( 'wp_enqueue_scripts', array( $manager, 'enqueue_frontend_scripts' ) ) ); - $this->assertEquals( 10, has_action( 'customize_save', array( $manager, 'check_customize_publish_authorization' ) ) ); - } - - /* - * For Customize_Snapshot_Manager_Back_Compat::Customize_Snapshot_Manager_Back_Compat(), see Test_Ajax_Customize_Snapshot_Manager_Back_Compat::test_ajax_update_snapshot_cap_check(). - */ - - /** - * Test customize preview init. - * - * @see Customize_Snapshot_Manager::customize_preview_init() - */ - function test_customize_preview_init() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->assertFalse( has_action( 'wp_enqueue_scripts', array( $manager, 'enqueue_preview_scripts' ) ) ); - $manager->customize_preview_init(); - $this->assertEquals( 10, has_action( 'wp_enqueue_scripts', array( $manager, 'enqueue_preview_scripts' ) ) ); - } - - /** - * Test enqueue preview scripts. - * - * @see Customize_Snapshot_Manager::enqueue_preview_scripts() - */ - function test_enqueue_preview_scripts() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->ensure_customize_manager(); - $manager->init(); - $handle = 'customize-snapshots-preview'; - $this->assertFalse( wp_scripts()->query( $handle, 'enqueued' ) ); - $this->assertFalse( wp_styles()->query( $handle, 'enqueued' ) ); - $manager->enqueue_preview_scripts(); - $this->assertTrue( wp_scripts()->query( $handle, 'enqueued' ) ); - $this->assertTrue( wp_styles()->query( $handle, 'enqueued' ) ); - - $after = wp_scripts()->get_data( $handle, 'after' ); - $this->assertNotEmpty( $after ); - $this->assertContains( 'CustomizeSnapshotsPreview', join( '', $after ) ); - } - - /** - * Test enqueue frontend scripts. - * - * @see Customize_Snapshot_Manager::enqueue_frontend_scripts() - */ - function test_enqueue_frontend_scripts() { - $this->plugin->register_scripts( wp_scripts() ); - $this->plugin->register_styles( wp_styles() ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertFalse( wp_script_is( 'customize-snapshots-frontend', 'enqueued' ) ); - $manager->enqueue_frontend_scripts(); - $this->assertFalse( wp_script_is( 'customize-snapshots-frontend', 'enqueued' ) ); - - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertFalse( wp_script_is( 'customize-snapshots-frontend', 'enqueued' ) ); - $manager->enqueue_frontend_scripts(); - $this->assertTrue( wp_script_is( 'customize-snapshots-frontend', 'enqueued' ) ); - } - - /** - * Test filter_customize_refresh_nonces. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager_Back_Compat::filter_customize_refresh_nonces() - */ - function test_filter_customize_refresh_nonces() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->assertArrayHasKey( 'snapshot', $manager->filter_customize_refresh_nonces( array() ) ); - } - - /** - * Tests show_theme_switch_error. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager_Back_Compat::show_theme_switch_error() - */ - function test_show_theme_switch_error() { - $this->markTestIncomplete(); - } - - /** - * Test publish snapshot with customize_save_after. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager_Back_Compat::publish_snapshot_with_customize_save_after() - */ - function test_publish_snapshot_with_customize_save_after() { - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - $_POST = array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'customize_snapshot_uuid' => self::UUID, - 'customized' => '{"foo":"foo_default","bar":"bar_default"}', - ); - $_REQUEST['action'] = 'customize_save'; - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $this->assertEmpty( $manager->snapshot()->post() ); - $manager->publish_snapshot_with_customize_save_after(); - $this->assertNotEmpty( $manager->snapshot()->post() ); - - $this->markTestIncomplete( 'Need to test when snapshot->save() returns errors, and when snapshot post save fails.' ); - } - - /** - * Test save_settings_with_publish_snapshot. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager_Back_Compat::save_settings_with_publish_snapshot() - */ - public function test_save_settings_with_publish_snapshot() { - $post_type = $this->manager->post_type; - $data = array( - 'blogdescription' => array( 'value' => 'Snapshot blog' ), - 'unknown_setting_foo' => array( 'value' => 'bar' ), - 'null_value_baz' => array( 'value' => null ), - 'foo' => array( 'value' => 'foo' ), - ); - $validate_data = array( - 'blogdescription' => array( 'value' => 'Snapshot blog' ), - 'unknown_setting_foo' => array( - 'value' => 'bar', - 'publish_error' => 'unrecognized_setting', - ), - 'null_value_baz' => array( - 'value' => null, - 'publish_error' => 'null_value', - ), - 'foo' => array( - 'value' => 'foo', - ), - ); - - if ( method_exists( 'WP_Customize_Setting', 'validate' ) ) { - $validate_data['foo']['publish_error'] = 'you_shell_not_pass'; - add_filter( 'customize_validate_foo', function( $validity ) { - $validity->add( 'you_shell_not_pass', 'Testing invalid setting while publishing snapshot' ); - return $validity; - }, 10, 1 ); - } - - $post_id = $post_type->save( array( - 'uuid' => self::UUID, - 'data' => $data, - 'status' => 'draft', - ) ); - - // Test invalid settings. - $post = get_post( $post_id ); - $this->manager->save_settings_with_publish_snapshot( 'publish', 'draft', $post ); - $post = get_post( $post_id ); - $this->assertEquals( $validate_data, json_decode( wp_unslash( $post->post_content ), true ) ); - $this->assertEquals( 'pending', $post->post_status ); - - // Test valid settings. - unset( $data['unknown_setting_foo'], $data['null_value_baz'], $data['foo'] ); - $post_id = $post_type->save( array( - 'uuid' => self::UUID, - 'data' => $data, - 'status' => 'publish', - ) ); - $this->assertEquals( 'publish', get_post_status( $post_id ) ); - $this->assertEquals( 'Snapshot blog', get_bloginfo( 'description' ) ); - } - - /* - * For Customize_Snapshot_Manager::handle_update_snapshot_request(), see Test_Ajax_Customize_Snapshot_Manager_Back_Compat. - */ - - /** - * Tests ensure_customize_manager. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::ensure_customize_manager() - */ - public function test_ensure_customize_manager() { - global $wp_customize; - $wp_customize = null; // WPCS: global override ok. - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->assertEmpty( $manager->customize_manager ); - $manager->ensure_customize_manager(); - $this->assertInstanceOf( 'WP_Customize_Manager', $manager->customize_manager ); - $this->assertInstanceOf( 'WP_Customize_Manager', $wp_customize ); - } - - /** - * Test enqueue controls scripts. - * - * @see Customize_Snapshot_Manager::enqueue_controls_scripts() - */ - function test_enqueue_controls_scripts() { - $this->plugin->register_scripts( wp_scripts() ); - $this->plugin->register_styles( wp_styles() ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $manager->enqueue_controls_scripts(); - $this->assertTrue( wp_script_is( 'customize-snapshots-compat', 'enqueued' ) ); - $this->assertTrue( wp_style_is( 'customize-snapshots', 'enqueued' ) ); - } - - /** - * Tests import_snapshot_data. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::import_snapshot_data() - */ - public function test_import_snapshot_data() { - global $wp_actions; - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $this->manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( - 'blogname' => array( 'value' => 'Hello' ), - 'blogdescription' => array( 'value' => null ), - ), - 'status' => 'draft', - ) ); - - // Prevent init from calling import_snapshot_data straight away. - unset( $wp_actions['setup_theme'] ); - - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $manager->ensure_customize_manager(); - do_action( 'customize_register', $manager->customize_manager ); - - $this->assertArrayNotHasKey( 'customized', $_POST ); - $this->assertArrayNotHasKey( 'customized', $_REQUEST ); - $this->assertArrayNotHasKey( 'blogname', $manager->customize_manager->unsanitized_post_values() ); - $this->assertArrayNotHasKey( 'blogdescription', $manager->customize_manager->unsanitized_post_values() ); - $manager->import_snapshot_data(); - $this->assertArrayHasKey( 'customized', $_POST ); - $this->assertArrayHasKey( 'customized', $_REQUEST ); - $this->assertArrayHasKey( 'blogname', $manager->customize_manager->unsanitized_post_values() ); - $this->assertArrayNotHasKey( 'blogdescription', $manager->customize_manager->unsanitized_post_values() ); - } - - /** - * Tests should_import_and_preview_snapshot. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::should_import_and_preview_snapshot() - */ - public function test_should_import_and_preview_snapshot() { - global $pagenow, $wp_customize; - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = $this->plugin->customize_snapshot_manager; - $post_id = $manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( 'blogname' => array( 'value' => 'Foo' ) ), - ) ); - $snapshot = new Customize_Snapshot_Back_Compat( $manager, self::UUID ); - - // Not if admin. - set_current_screen( 'posts' ); - $pagenow = 'posts.php'; // WPCS: global override ok. - $this->assertTrue( is_admin() ); - $this->assertFalse( $manager->should_import_and_preview_snapshot( $snapshot ) ); - - // Not if theme switch error. - set_current_screen( 'customize' ); - $pagenow = 'customize.php'; // WPCS: global override ok. - update_post_meta( $post_id, '_snapshot_theme', 'Foo' ); - $this->assertFalse( $manager->should_import_and_preview_snapshot( $snapshot ) ); - delete_post_meta( $post_id, '_snapshot_theme' ); - - // Not if customize_save. - $_REQUEST['action'] = 'customize_save'; - $this->assertFalse( $manager->should_import_and_preview_snapshot( $snapshot ) ); - unset( $_REQUEST['action'] ); - - // Not if published snapshot. - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'status' => 'publish', - ) ); - $this->assertFalse( $manager->should_import_and_preview_snapshot( $snapshot ) ); - $manager->post_type->save( array( - 'uuid' => self::UUID, - 'status' => 'draft', - ) ); - - // Not if unsanitized post values is not empty. - $manager->customize_manager = new \WP_Customize_Manager(); - $wp_customize = $manager->customize_manager; // WPCS: global override ok. - $wp_customize->set_post_value( 'name', 'value' ); - $this->assertNotEmpty( $manager->customize_manager->unsanitized_post_values() ); - $this->assertFalse( $manager->should_import_and_preview_snapshot( $snapshot ) ); - - // OK. - $manager->customize_manager = new \WP_Customize_Manager(); - $wp_customize = $manager->customize_manager; // WPCS: global override ok. - $this->assertTrue( $manager->should_import_and_preview_snapshot( $snapshot ) ); - } - - /** - * Tests get_theme_switch_error. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::get_theme_switch_error() - */ - function test_get_theme_switch_error() { - $this->markTestIncomplete(); - } - - /** - * Tests is_previewing_settings. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::is_previewing_settings() - */ - public function test_is_previewing_settings() { - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $this->plugin->customize_snapshot_manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( 'blogname' => array( 'value' => 'Foo' ) ), - ) ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $manager->preview_snapshot_settings(); - $this->assertTrue( $manager->is_previewing_settings() ); - } - - /** - * Tests is_previewing_settings. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::is_previewing_settings() - */ - public function test_is_previewing_settings_via_preview_init() { - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->assertFalse( $manager->is_previewing_settings() ); - do_action( 'customize_preview_init' ); - $this->assertTrue( $manager->is_previewing_settings() ); - } - - /** - * Tests preview_snapshot_settings. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::preview_snapshot_settings() - */ - public function test_preview_snapshot_settings() { - global $wp_actions; - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $this->manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( - 'blogname' => array( 'value' => 'Hello' ), - ), - 'status' => 'draft', - ) ); - - // Prevent init from calling preview_snapshot_settings straight away. - unset( $wp_actions['wp_loaded'] ); - - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - $manager->ensure_customize_manager(); - do_action( 'customize_register', $manager->customize_manager ); - $this->assertFalse( $manager->is_previewing_settings() ); - $this->assertFalse( $manager->customize_manager->get_setting( 'blogname' )->dirty ); - $this->assertNotEquals( 'Hello', get_option( 'blogname' ) ); - $manager->preview_snapshot_settings(); - $this->assertEquals( 'Hello', get_option( 'blogname' ) ); - $this->assertTrue( $manager->customize_manager->get_setting( 'blogname' )->dirty ); - } - - /** - * Tests add_widget_setting_preview_filters. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::add_widget_setting_preview_filters() - */ - public function test_add_widget_setting_preview_filters() { - $this->markTestIncomplete(); - } - - /** - * Tests add_nav_menu_setting_preview_filters. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::add_nav_menu_setting_preview_filters() - */ - public function test_add_nav_menu_setting_preview_filters() { - $this->markTestIncomplete(); - } - - /** - * Tests preview_early_nav_menus_in_customizer. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::preview_early_nav_menus_in_customizer() - */ - public function test_preview_early_nav_menus_in_customizer() { - global $pagenow; - $pagenow = 'customize.php'; // WPCS: Global override ok. - set_current_screen( 'customize' ); - - $menu_id = -123; - $setting_id = sprintf( 'nav_menu[%d]', $menu_id ); - - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $this->manager->post_type->save( array( - 'uuid' => self::UUID, - 'data' => array( - $setting_id => array( - 'value' => array( - 'name' => 'Bar', - ), - ), - ), - 'status' => 'draft', - ) ); - - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - do_action( 'customize_register', $manager->customize_manager ); - - $setting = $manager->customize_manager->get_setting( $setting_id ); - $this->assertInstanceOf( 'WP_Customize_Nav_Menu_Setting', $setting ); - $nav_menu = wp_get_nav_menu_object( $menu_id ); - $this->assertEquals( 'Bar', $nav_menu->name ); - - $this->assertInstanceOf( 'WP_Customize_Nav_Menu_Section', $manager->customize_manager->get_section( $setting_id ) ); - } - - /** - * Tests setup_preview_ajax_requests. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::init() - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::setup_preview_ajax_requests() - */ - public function test_setup_preview_ajax_requests() { - wp_set_current_user( $this->user_id ); - $_REQUEST['wp_customize_preview_ajax'] = 'true'; - $_POST['customized'] = wp_slash( wp_json_encode( array( 'blogname' => 'Foo' ) ) ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->do_customize_boot_actions( true ); - $this->assertTrue( is_customize_preview() ); - $manager->init(); - $this->assertEquals( 12, has_action( 'wp_loaded', array( $manager, 'setup_preview_ajax_requests' ) ) ); - do_action( 'wp_loaded' ); - - $this->assertFalse( has_action( 'shutdown', array( $this->wp_customize, 'customize_preview_signature' ) ) ); - $this->assertEquals( 5, has_action( 'parse_request', array( $manager, 'override_request_method' ) ) ); - } - - - /** - * Tests setup_preview_ajax_requests for admin_ajax. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::init() - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::setup_preview_ajax_requests() - */ - public function test_setup_preview_ajax_requests_for_admin_ajax() { - global $pagenow; - wp_set_current_user( $this->user_id ); - - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'GET'; - $pagenow = 'admin-ajax.php'; // WPCS: Global override ok. - set_current_screen( 'admin-ajax' ); - $this->assertTrue( is_admin() ); - - $_REQUEST['wp_customize_preview_ajax'] = 'true'; - $_POST['customized'] = wp_slash( wp_json_encode( array( 'blogname' => 'Foo' ) ) ); - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - do_action( 'admin_init' ); - $this->do_customize_boot_actions( true ); - $this->assertTrue( is_customize_preview() ); - $this->assertFalse( has_action( 'shutdown', array( $this->wp_customize, 'customize_preview_signature' ) ) ); - $this->assertFalse( has_action( 'parse_request', array( $manager, 'override_request_method' ) ) ); - $this->assertEquals( 'GET', $_SERVER['REQUEST_METHOD'] ); - $this->assertEquals( 'Foo', get_option( 'blogname' ) ); - } - - /** - * Tests override_request_method. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::override_request_method() - */ - public function test_override_request_method() { - global $wp; - - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $this->assertFalse( $manager->override_request_method() ); - - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'GET'; - $wp->query_vars['rest_route'] = '/wp/v1/foo'; - $this->assertFalse( $manager->override_request_method() ); - unset( $wp->query_vars['rest_route'] ); - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'GET'; - $this->assertFalse( $manager->override_request_method() ); - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'BAD'; - $this->assertFalse( $manager->override_request_method() ); - - $_GET = wp_slash( array( 'foo' => '1' ) ); - $_POST = wp_slash( array( 'bar' => '2' ) ); - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'GET'; - $this->assertTrue( $manager->override_request_method() ); - $this->assertEquals( 'GET', $_SERVER['REQUEST_METHOD'] ); - $this->assertEquals( 'foo=1&bar=2', $_SERVER['QUERY_STRING'] ); - $this->assertArrayHasKey( 'foo', $_GET ); - $this->assertArrayHasKey( 'bar', $_GET ); - - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; - $this->assertFalse( $manager->override_request_method() ); - } - - /** - * Test customize menu. - * - * @see Customize_Snapshot_Manager::customize_menu() - */ - public function test_customize_menu() { - set_current_screen( 'front' ); - $preview_url = home_url( '/' ); - - $_REQUEST['customize_snapshot_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager_Back_Compat( $this->plugin ); - $manager->init(); - - require_once( ABSPATH . WPINC . '/class-wp-admin-bar.php' ); - $wp_admin_bar = new \WP_Admin_Bar(); // WPCS: Override OK. - $this->assertInstanceOf( 'WP_Admin_Bar', $wp_admin_bar ); - - wp_set_current_user( $this->user_id ); - $this->go_to( home_url( '?customize_snapshot_uuid=' . self::UUID ) ); - $wp_admin_bar->initialize(); - $wp_admin_bar->add_menus(); - - do_action_ref_array( 'admin_bar_menu', array( &$wp_admin_bar ) ); - $parsed_url = wp_parse_url( $wp_admin_bar->get_node( 'customize' )->href ); - $query_params = array(); - wp_parse_str( $parsed_url['query'], $query_params ); - $this->assertEquals( $preview_url, $query_params['url'] ); - $this->assertEquals( self::UUID, $query_params['customize_snapshot_uuid'] ); - } - -} diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php index 953fa736..bf807337 100644 --- a/tests/php/test-class-customize-snapshot-manager.php +++ b/tests/php/test-class-customize-snapshot-manager.php @@ -74,13 +74,11 @@ class Test_Customize_Snapshot_Manager extends \WP_UnitTestCase { function setUp() { parent::setUp(); $this->plugin = get_plugin_instance(); - if ( $this->plugin->compat ) { - $this->front_param = 'customize_snapshot_uuid'; - } else { - $this->front_param = 'customize_changeset_uuid'; - } + $this->front_param = 'customize_changeset_uuid'; require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $GLOBALS['wp_customize'] = new \WP_Customize_Manager(); // WPCS: global override ok. + $GLOBALS['wp_customize'] = new \WP_Customize_Manager( array( + 'changeset_uuid' => self::UUID, + ) ); // WPCS: global override ok. $this->wp_customize = $GLOBALS['wp_customize']; $this->wp_customize->add_setting( 'foo', array( 'default' => 'foo_default' ) ); @@ -158,23 +156,10 @@ function do_customize_boot_actions( $on = false ) { * * @param Plugin $plugin Plugin object. * - * @return Customize_Snapshot_Manager|Customize_Snapshot_Manager_Back_Compat Manager new instace. + * @return Customize_Snapshot_Manager Manager new instace. */ function get_snapshot_manager_instance( $plugin ) { - if ( $this->plugin->compat ) { - return new Customize_Snapshot_Manager_Back_Compat( $plugin ); - } else { - return new Customize_Snapshot_Manager( $plugin ); - } - } - - /** - * Mark test incomplete as it is only for new versions. - */ - public function mark_incompatible() { - if ( $this->plugin->compat ) { - $this->markTestSkipped( 'This unit-test require WP version 4.7 or up.' ); - } + return new Customize_Snapshot_Manager( $plugin ); } /** @@ -183,30 +168,8 @@ public function mark_incompatible() { * @see Customize_Snapshot_Manager::__construct() */ function test_construct_without_customize() { - $this->mark_incompatible(); $this->assertInstanceOf( 'CustomizeSnapshots\Customize_Snapshot_Manager', $this->manager ); $this->assertInstanceOf( 'CustomizeSnapshots\Plugin', $this->manager->plugin ); - $this->assertNull( $this->manager->current_snapshot_uuid ); - } - - /** - * Test constructor with Customizer. - * - * @see Customize_Snapshot_Manager::__construct() - */ - function test_construct_with_customize() { - $this->mark_incompatible(); - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - $this->assertTrue( is_customize_preview() ); - $_REQUEST[ $this->front_param ] = self::UUID; - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $manager->init(); - $this->assertEquals( $manager->current_snapshot_uuid, self::UUID ); - $this->assertInstanceOf( 'CustomizeSnapshots\Post_Type', $manager->post_type ); - $this->assertInstanceOf( 'CustomizeSnapshots\Customize_Snapshot', $manager->snapshot() ); - $this->assertEquals( 0, has_action( 'init', array( $manager, 'create_post_type' ) ) ); - $this->assertEquals( 10, has_action( 'customize_controls_enqueue_scripts', array( $manager, 'enqueue_controls_scripts' ) ) ); } /** @@ -253,46 +216,28 @@ function test_hooks() { * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::init() */ public function test_init_hooks() { - $this->mark_incompatible(); $manager = $this->get_snapshot_manager_instance( $this->plugin ); $manager->init(); $this->assertEquals( 10, has_filter( 'customize_save_response', array( $manager, 'add_snapshot_var_to_customize_save' ) ) ); $this->assertInstanceOf( __NAMESPACE__ . '\Post_Type', $manager->post_type ); } - /** - * Tests load_snapshot. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::load_snapshot() - */ - public function test_load_snapshot() { - $this->mark_incompatible(); - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $this->assertNull( $manager->snapshot ); - $this->assertNull( $manager->customize_manager ); - $manager->load_snapshot(); - $this->assertInstanceOf( __NAMESPACE__ . '\\Customize_Snapshot', $manager->snapshot ); - $this->assertInstanceOf( '\WP_Customize_Manager', $manager->customize_manager ); - } /** * Tests add_snapshot_var_to_customize_save. * * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::add_snapshot_var_to_customize_save() */ public function test_add_snapshot_var_to_customize_save() { - $this->mark_incompatible(); global $wp_customize; - $uuid = wp_generate_uuid4(); + $changeset_uuid = wp_generate_uuid4(); get_plugin_instance()->customize_snapshot_manager->post_type->save( array( - 'uuid' => $uuid, + 'uuid' => $changeset_uuid, 'data' => array(), 'status' => 'draft', ) ); + $wp_customize = new \WP_Customize_Manager( compact( 'changeset_uuid' ) ); $manager = new Customize_Snapshot_Manager( $this->plugin ); - $wp_customize = null; // WPCS: global override ok. - $manager->current_snapshot_uuid = $uuid; - $manager->load_snapshot(); - $data = $manager->add_snapshot_var_to_customize_save( array(), $manager->customize_manager ); + $data = $manager->add_snapshot_var_to_customize_save( array(), $manager->ensure_customize_manager() ); $this->assertArrayHasKey( 'edit_link', $data ); $this->assertArrayHasKey( 'publish_date', $data ); $this->assertArrayHasKey( 'title', $data ); @@ -307,33 +252,6 @@ public function test_enqueue_admin_scripts() { $this->markTestIncomplete(); } - /** - * Tests init hooks. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::init() - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::read_current_snapshot_uuid() - */ - public function test_read_current_snapshot_uuid() { - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $manager->init(); - - $this->assertFalse( $manager->read_current_snapshot_uuid() ); - $this->assertNull( $manager->current_snapshot_uuid ); - - $_REQUEST[ $manager->get_customize_uuid_param() ] = 'bad'; - $this->assertFalse( $manager->read_current_snapshot_uuid() ); - $this->assertNull( $manager->current_snapshot_uuid ); - - $_REQUEST[ $manager->get_customize_uuid_param() ] = self::UUID; - $this->assertTrue( $manager->read_current_snapshot_uuid() ); - $this->assertEquals( self::UUID, $manager->current_snapshot_uuid ); - - $_REQUEST[ $manager->get_customize_uuid_param() ] = self::UUID; - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $manager->init(); - $this->assertEquals( self::UUID, $manager->current_snapshot_uuid ); - } - /** * Tests doing_customize_save_ajax. * @@ -358,26 +276,10 @@ public function test_doing_customize_save_ajax() { public function test_ensure_customize_manager() { global $wp_customize; $wp_customize = null; // WPCS: global override ok. - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $this->assertEmpty( $manager->customize_manager ); - $manager->ensure_customize_manager(); - $this->assertInstanceOf( 'WP_Customize_Manager', $manager->customize_manager ); - $this->assertInstanceOf( 'WP_Customize_Manager', $wp_customize ); - } - - /** - * Tests is_theme_active. - * - * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::is_theme_active() - */ - public function test_is_theme_active() { - global $wp_customize; - $wp_customize = null; // WPCS: global override ok. - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $this->assertTrue( $manager->is_theme_active() ); - - $manager->ensure_customize_manager(); - $this->assertTrue( $manager->is_theme_active() ); + $snapshot_manager = $this->get_snapshot_manager_instance( $this->plugin ); + $customize_manager = $snapshot_manager->ensure_customize_manager(); + $this->assertInstanceOf( 'WP_Customize_Manager', $customize_manager ); + $this->assertSame( $customize_manager, $wp_customize ); } /** @@ -386,19 +288,21 @@ public function test_is_theme_active() { * @see Customize_Snapshot_Manager::add_snapshot_uuid_to_return_url() */ public function test_add_snapshot_uuid_to_return_url() { - global $wp_version; - if ( version_compare( $wp_version, '4.4-beta', '>=' ) ) { - wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); - $_GET[ $this->front_param ] = self::UUID; - $_REQUEST[ $this->front_param ] = self::UUID; - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $manager->init(); - $manager->ensure_customize_manager(); - do_action( 'setup_theme' ); - $this->assertNotContains( $this->front_param, $manager->customize_manager->get_return_url() ); - $manager->add_snapshot_uuid_to_return_url(); - $this->assertContains( $this->front_param, $manager->customize_manager->get_return_url() ); - } + global $wp_customize; + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); + + $manager = $this->get_snapshot_manager_instance( $this->plugin ); + $wp_customize = $manager->ensure_customize_manager(); + $_SERVER['HTTP_REFERER'] = wp_slash( add_query_arg( + 'customize_changeset_uuid', + $wp_customize->changeset_uuid(), + home_url( '/' ) + ) ); + $wp_customize->start_previewing_theme(); + $wp_customize->set_return_url( home_url( '/' ) ); + $this->assertNotContains( $this->front_param, $wp_customize->get_return_url() ); + $manager->add_snapshot_uuid_to_return_url(); + $this->assertContains( $this->front_param, $wp_customize->get_return_url() ); } /** @@ -429,18 +333,6 @@ function test_enqueue_controls_scripts() { $this->assertTrue( wp_style_is( 'customize-snapshots', 'enqueued' ) ); } - /** - * Test snapshot method. - * - * @see Customize_Snapshot_Manager::snapshot() - */ - function test_snapshot() { - $_REQUEST[ $this->front_param ] = self::UUID; - $manager = $this->get_snapshot_manager_instance( $this->plugin ); - $manager->init(); - $this->assertInstanceOf( 'CustomizeSnapshots\Customize_Snapshot', $manager->snapshot() ); - } - /** * Test prepare_snapshot_post_content_for_publish. * @@ -583,10 +475,19 @@ public function test_add_post_edit_and_exit_links() { remove_all_actions( 'admin_bar_menu' ); $manager = $this->get_snapshot_manager_instance( $this->plugin ); $_REQUEST[ $this->front_param ] = self::UUID; - if ( ! $this->plugin->compat ) { - global $wp_customize; - $wp_customize = null; // WPCS: Override OK. - } + + global $wp_customize; + $wp_customize = $manager->ensure_customize_manager(); // WPCS: Override OK. + $wp_customize->start_previewing_theme(); + $wp_customize->register_controls(); + $wp_customize->save_changeset_post( array( + 'data' => array( + 'blogname' => array( + 'value' => 'Blogname', + ), + ), + ) ); + $manager->init(); $wp_admin_bar = new \WP_Admin_Bar(); // WPCS: Override OK. $wp_admin_bar->initialize(); @@ -659,12 +560,7 @@ public function test_override_post_date_default_data() { * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::get_post_type() */ public function test_get_post_type() { - $plugin = get_plugin_instance(); - if ( $plugin->compat ) { - $this->assertEquals( $this->manager->get_post_type(), Post_Type_Back_Compat::SLUG ); - } else { - $this->assertEquals( $this->manager->get_post_type(), Post_Type::SLUG ); - } + $this->assertEquals( $this->manager->get_post_type(), Post_Type::SLUG ); } /** @@ -673,12 +569,7 @@ public function test_get_post_type() { * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::get_front_uuid_param() */ public function test_get_front_uuid_param() { - $plugin = get_plugin_instance(); - if ( $plugin->compat ) { - $this->assertEquals( $this->manager->get_front_uuid_param(), Post_Type_Back_Compat::FRONT_UUID_PARAM_NAME ); - } else { - $this->assertEquals( $this->manager->get_front_uuid_param(), Post_Type::FRONT_UUID_PARAM_NAME ); - } + $this->assertEquals( $this->manager->get_front_uuid_param(), Post_Type::FRONT_UUID_PARAM_NAME ); } /** @@ -687,12 +578,7 @@ public function test_get_front_uuid_param() { * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::get_customize_uuid_param() */ public function test_get_customize_uuid_param() { - $plugin = get_plugin_instance(); - if ( $plugin->compat ) { - $this->assertEquals( $this->manager->get_customize_uuid_param(), Post_Type_Back_Compat::CUSTOMIZE_UUID_PARAM_NAME ); - } else { - $this->assertEquals( $this->manager->get_customize_uuid_param(), Post_Type::CUSTOMIZE_UUID_PARAM_NAME ); - } + $this->assertEquals( $this->manager->get_customize_uuid_param(), Post_Type::CUSTOMIZE_UUID_PARAM_NAME ); } /** @@ -714,6 +600,7 @@ public function test_replace_customize_link() { $manager = $this->get_snapshot_manager_instance( $this->plugin ); $manager->init(); + $manager->ensure_customize_manager()->start_previewing_theme(); // Ensure customize link remains unknown if user lacks cap. wp_set_current_user( 0 ); @@ -795,7 +682,7 @@ public function test_save_customizer_state_query_vars() { /** * Test clean_up_nav_menus_created_auto_drafts * - * @convers \CustomizeSnapshots\Customize_Snapshot_Manager::clean_up_nav_menus_created_auto_drafts() + * @covers \CustomizeSnapshots\Customize_Snapshot_Manager::clean_up_nav_menus_created_auto_drafts() */ public function test_clean_up_nav_menus_created_auto_drafts() { $nav_created_post_ids = $this->factory()->post->create_many( 2, array( diff --git a/tests/php/test-class-customize-snapshot.php b/tests/php/test-class-customize-snapshot.php deleted file mode 100644 index fe934942..00000000 --- a/tests/php/test-class-customize-snapshot.php +++ /dev/null @@ -1,155 +0,0 @@ -plugin = get_plugin_instance(); - $this->mark_incompatible(); - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $GLOBALS['wp_customize'] = new \WP_Customize_Manager( array( 'changeset_uuid' => self::UUID ) ); // WPCS: override ok. - $this->snapshot_manager = new Customize_Snapshot_Manager( $this->plugin ); - $this->snapshot_manager->post_type = new Post_Type( $this->snapshot_manager ); - $this->wp_customize = $GLOBALS['wp_customize']; - } - - /** - * Tear down. - */ - function tearDown() { - $this->wp_customize = null; - unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['wp_scripts'] ); - parent::tearDown(); - } - - /** - * Test UUID. - * - * @covers \CustomizeSnapshots\Customize_Snapshot::uuid() - */ - function test_uuid() { - $_REQUEST['customize_changeset_uuid'] = self::UUID; - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->init(); - $this->assertEquals( self::UUID, $manager->snapshot()->uuid() ); - } - - /** - * Test get_edit_link. - * - * @covers \CustomizeSnapshots\Customize_Snapshot::get_edit_link() - */ - function test_get_edit_link() { - wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); - $post_id = $this->snapshot_manager->post_type->save( array( - 'uuid' => self::UUID, - 'status' => 'draft', - 'data' => array(), - ) ); - $has_filter = has_filter( 'get_edit_post_link', '__return_empty_string' ); - if ( ! $has_filter ) { - add_filter( 'get_edit_post_link', '__return_empty_string' ); - } - $snapshot = new Customize_Snapshot( $this->snapshot_manager ); - $link = $snapshot->get_edit_link( $post_id ); - $this->assertContains( 'post=' . $post_id, $link ); - } - - /** - * Test post - * - * @covers \CustomizeSnapshots\Customize_Snapshot::post() - */ - function test_post() { - $post_id = $this->snapshot_manager->post_type->save( array( - 'uuid' => self::UUID, - 'status' => 'draft', - 'data' => array(), - ) ); - $this->snapshot_manager->customize_manager = new \WP_Customize_Manager( array( - 'changeset_uuid' => self::UUID, - ) ); - $snapshot = new Customize_Snapshot( $this->snapshot_manager ); - $snapshot_post = $snapshot->post(); - $this->assertEquals( $post_id, $snapshot_post->ID ); - $this->snapshot_manager->customize_manager = new \WP_Customize_Manager( array( - 'changeset_uuid' => wp_generate_uuid4(), - ) ); - $snapshot_post = $snapshot->post(); - $this->assertNull( $snapshot_post ); - } - - /** - * Mark test incomplete as it is only for new versions. - */ - public function mark_incompatible() { - if ( $this->plugin->compat ) { - $this->markTestSkipped( 'This unit-test require WP version 4.7 or up.' ); - } - } -} diff --git a/tests/php/test-class-migrate.php b/tests/php/test-class-migrate.php deleted file mode 100644 index 204da93b..00000000 --- a/tests/php/test-class-migrate.php +++ /dev/null @@ -1,263 +0,0 @@ -plugin = get_plugin_instance(); - $this->mark_incompatible(); - $this->snapshot_manager = new Customize_Snapshot_Manager( $this->plugin ); - $this->snapshot_manager->post_type = new Post_Type( $this->snapshot_manager ); - } - - /** - * Mark test incomplete as it is only for new versions. - */ - public function mark_incompatible() { - if ( $this->plugin->compat ) { - $this->markTestSkipped( 'This unit-test require WP version 4.7 or up.' ); - } - } - - /** - * Tear down. - */ - function tearDown() { - $this->wp_customize = null; - unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['wp_scripts'] ); - update_option( Migrate::KEY, 0 ); - parent::tearDown(); - } - - /** - * Test Migrate constructor. - * - * @see Migrate::__construct() - */ - function test_construct() { - $class_name = 'CustomizeSnapshots\Migrate'; - $mock = $this->getMockBuilder( $class_name ) - ->disableOriginalConstructor() - ->getMock(); - $mock->expects( $this->once() ) - ->method( 'maybe_migrate' ); - $reflected_class = new \ReflectionClass( $class_name ); - $constructor = $reflected_class->getConstructor(); - $constructor->invoke( $mock, $this->plugin ); - set_current_screen( 'index' ); - $constructor->invoke( $mock, $this->plugin ); - $user_id = $this->factory()->user->create( array( - 'role' => 'administrator', - ) ); - if ( is_multisite() ) { - grant_super_admin( $user_id ); - } - wp_set_current_user( $user_id ); - $constructor->invoke( $mock, $this->plugin ); - } - - /** - * Test is_migrated. - * - * @see Migrate::is_migrated() - */ - function test_is_migrated() { - $migrate = new Migrate( $this->plugin ); - update_option( Migrate::KEY, 0 ); - $this->assertFalse( $migrate->is_migrated() ); - update_option( Migrate::KEY, 1 ); - $this->assertTrue( $migrate->is_migrated() ); - } - - /** - * Test maybe_migrate. - * - * @see Migrate::maybe_migrate() - */ - function test_maybe_migrate() { - delete_option( Migrate::KEY ); - $migrate = $this->getMockBuilder( 'CustomizeSnapshots\Migrate' ) - ->setMethods( array( 'changeset_migrate' ) ) - ->setConstructorArgs( array( $this->plugin ) ) - ->getMock(); - $migrate->expects( $this->once() ) - ->method( 'changeset_migrate' ) - ->with( 1, true ) - ->will( $this->returnValue( 92 ) ); - $migrate->maybe_migrate(); - $this->assertEquals( 10, has_action( 'admin_notices', array( $migrate, 'show_migration_notice' ) ) ); - $this->assertEquals( 10, has_action( 'admin_enqueue_scripts', array( $migrate, 'enqueue_script' ) ) ); - $this->assertEquals( 10, has_action( 'wp_ajax_customize_snapshot_migration', array( $migrate, 'handle_migrate_changeset_request' ) ) ); - - // If no post to migrate fall back and add option value. - $migrate_obj = $this->getMockBuilder( 'CustomizeSnapshots\Migrate' ) - ->setMethods( array( 'changeset_migrate' ) ) - ->setConstructorArgs( array( $this->plugin ) ) - ->getMock(); - $migrate_obj->expects( $this->once() ) - ->method( 'changeset_migrate' ) - ->with( 1, true ) - ->will( $this->returnValue( false ) ); - $migrate_obj->maybe_migrate(); - $this->assertNotEquals( 10, has_action( 'admin_notices', array( $migrate_obj, 'show_migration_notice' ) ) ); - $this->assertNotEquals( 10, has_action( 'admin_enqueue_scripts', array( $migrate_obj, 'enqueue_script' ) ) ); - $this->assertNotEquals( 10, has_action( 'wp_ajax_customize_snapshot_migration', array( $migrate_obj, 'handle_migrate_changeset_request' ) ) ); - $this->assertEquals( 1, get_option( Migrate::KEY ) ); - } - - /** - * Test show_migration_notice. - * - * @see Migrate::show_migration_notice() - */ - function test_show_migration_notice() { - $migrate = new Migrate( $this->plugin ); - ob_start(); - $migrate->show_migration_notice(); - $data = ob_get_clean(); - $this->assertContains( 'customize-snapshot-migration', $data ); - $this->assertContains( 'customize-snapshot-migration', $data ); - $this->assertContains( 'data-nonce', $data ); - $this->assertContains( 'data-migration-success', $data ); - $this->assertContains( 'customize-snapshot-spinner', $data ); - } - - /** - * Test changeset_migrate. - * - * @see Migrate::changeset_migrate() - */ - function test_changeset_migrate() { - $old_post_type_obj = new Post_Type_Back_Compat( $this->snapshot_manager ); - $post_id = $old_post_type_obj->save( array( - 'uuid' => wp_generate_uuid4(), - 'status' => 'draft', - 'data' => array(), - ) ); - $migrate = new Migrate( $this->plugin ); - $posts_count = $migrate->changeset_migrate( -1, true ); - $this->assertEquals( $post_id, array_shift( $posts_count ) ); - - $migrate_obj = $this->getMockBuilder( 'CustomizeSnapshots\Migrate' ) - ->setMethods( array( 'migrate_post' ) ) - ->setConstructorArgs( array( $this->plugin ) ) - ->getMock(); - $migrate_obj->expects( $this->once() ) - ->method( 'migrate_post' ) - ->will( $this->returnValue( null ) ); - $migrate_obj->changeset_migrate( -1 ); - } - - /** - * Test migrate_post. - * - * @see Migrate::migrate_post() - */ - function test_migrate_post() { - $admin_user_id = $this->factory()->user->create( array( - 'role' => 'administrator', - ) ); - wp_set_current_user( $admin_user_id ); - $old_post_type_obj = new Post_Type_Back_Compat( $this->snapshot_manager ); - $snapshot_post_id = $old_post_type_obj->save( array( - 'uuid' => wp_generate_uuid4(), - 'status' => 'draft', - 'data' => array( - 'foo' => array( - 'value' => 'bar', - ), - ), - ) ); - add_post_meta( $snapshot_post_id, '_snapshot_theme', 'foo_theme' ); - require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); - $wp_customize = new \WP_Customize_Manager( array( - 'changeset_uuid' => self::UUID, - ) ); - - $wp_customize->add_setting( 'foo', array( - 'default' => 'foo_default', - ) ); - $this->action_customize_register_for_dynamic_settings(); - - $migrate = new Migrate( $this->plugin ); - - $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); - if ( $has_kses ) { - kses_remove_filters(); // Prevent KSES from corrupting JSON in post_content. - } - $migrate->migrate_post( $snapshot_post_id ); - if ( $has_kses ) { - kses_init_filters(); - } - - $changeset_post = get_post( $snapshot_post_id ); - $this->assertEquals( Post_Type::SLUG, $changeset_post->post_type ); - $data = json_decode( $changeset_post->post_content, true ); - $expected = array( - 'foo_theme::foo' => array( - 'value' => 'bar', - 'user_id' => (string) $admin_user_id, - 'type' => 'theme_mod', - ), - ); - $this->assertSame( $expected, $data ); - } - - /** - * Add filter for dynamic setting. - */ - function action_customize_register_for_dynamic_settings() { - add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_customize_dynamic_setting_args_for_test_dynamic_settings' ), 10, 2 ); - } - - /** - * To support dynamic setting. - * - * @param array $setting_args Setting args. - * @param string $setting_id Setting ID. - * @return array - */ - function filter_customize_dynamic_setting_args_for_test_dynamic_settings( $setting_args, $setting_id ) { - if ( in_array( $setting_id, array( 'foo' ), true ) ) { - $setting_args = array( - 'default' => "dynamic_{$setting_id}_default", - ); - } - return $setting_args; - } -} diff --git a/tests/php/test-class-post-type-back-compat.php b/tests/php/test-class-post-type-back-compat.php deleted file mode 100644 index fb29ccfd..00000000 --- a/tests/php/test-class-post-type-back-compat.php +++ /dev/null @@ -1,295 +0,0 @@ -plugin = get_plugin_instance(); - if ( ! $this->plugin->compat ) { - $this->markTestSkipped( 'WordPress Version 4.6.x or below is required for this test-case.' ); - } - $GLOBALS['wp_customize'] = null; // WPCS: Global override ok. - unregister_post_type( Post_Type_Back_Compat::SLUG ); - } - - /** - * Test register post type. - * - * @see Post_Type::init() - */ - public function test_init() { - $this->assertFalse( post_type_exists( Post_Type_Back_Compat::SLUG ) ); - $post_type_obj = new Post_Type_Back_Compat( $this->plugin->customize_snapshot_manager ); - $this->plugin->customize_snapshot_manager->init(); - $post_type_obj->init(); - $this->assertTrue( post_type_exists( Post_Type_Back_Compat::SLUG ) ); - - $this->assertEquals( 10, has_action( 'admin_notices', array( $post_type_obj, 'show_publish_error_admin_notice' ) ) ); - $this->assertEquals( 10, has_filter( 'display_post_states', array( $post_type_obj, 'display_post_states' ) ) ); - $this->assertEquals( 10, has_action( 'admin_footer-edit.php', array( $post_type_obj, 'snapshot_merge_print_script' ) ) ); - $this->assertEquals( 10, has_action( 'load-edit.php', array( $post_type_obj, 'handle_snapshot_merge_workaround' ) ) ); - $this->assertEquals( 10, has_filter( 'post_type_link', array( $post_type_obj, 'filter_post_type_link' ) ) ); - $this->assertEquals( 10, has_filter( 'wp_insert_post_data', array( $post_type_obj, 'preserve_post_name_in_insert_data' ) ) ); - } - - /** - * Tests show_publish_error_admin_notice. - * - * @covers \CustomizeSnapshots\Post_Type_Back_Compat::show_publish_error_admin_notice() - */ - public function test_show_publish_error_admin_notice() { - global $current_screen, $post; - wp_set_current_user( $this->factory()->user->create( array( - 'role' => 'administrator', - ) ) ); - $post_type_obj = new Post_Type_Back_Compat( $this->plugin->customize_snapshot_manager ); - $post_type_obj->init(); - $post_id = $post_type_obj->save( array( - 'uuid' => self::UUID, - 'data' => array(), - ) ); - - ob_start(); - $post_type_obj->show_publish_error_admin_notice(); - $this->assertEmpty( ob_get_clean() ); - - $current_screen = \WP_Screen::get( 'customize_snapshot' ); // WPCS: Override ok. - $current_screen->id = 'customize_snapshot'; - $current_screen->base = 'edit'; - ob_start(); - $post_type_obj->show_publish_error_admin_notice(); - $this->assertEmpty( ob_get_clean() ); - - $current_screen->base = 'post'; - ob_start(); - $post_type_obj->show_publish_error_admin_notice(); - $this->assertEmpty( ob_get_clean() ); - - $_REQUEST['snapshot_error_on_publish'] = '1'; - wp_update_post( array( - 'ID' => $post_id, - 'post_status' => 'pending', - ) ); - $post = get_post( $post_id ); // WPCS: override ok. - ob_start(); - $post_type_obj->show_publish_error_admin_notice(); - $this->assertContains( 'notice-error', ob_get_clean() ); - } - - /** - * Tests display_post_states. - * - * @covers \CustomizeSnapshots\Post_Type_Back_Compat::display_post_states() - */ - public function test_display_post_states() { - $post_type_obj = new Post_Type_Back_Compat( $this->plugin->customize_snapshot_manager ); - - $post_id = $post_type_obj->save( array( - 'uuid' => self::UUID, - 'data' => array( 'foo' => array( 'value' => 'bar' ) ), - ) ); - $states = $post_type_obj->display_post_states( array(), get_post( $post_id ) ); - $this->assertArrayNotHasKey( 'snapshot_error', $states ); - - update_post_meta( $post_id, 'snapshot_error_on_publish', true ); - $states = $post_type_obj->display_post_states( array(), get_post( $post_id ) ); - $this->assertArrayHasKey( 'snapshot_error', $states ); - } - - /** - * Test snapshot_merge_print_script - * - * @see Post_Type_Back_Compat::snapshot_merge_print_script() - */ - public function test_snapshot_merge_print_script() { - global $post_type; - $post_type = Post_Type_Back_Compat::SLUG; // WPCS: global override ok. - $post_type_obj = new Post_Type_Back_Compat( $this->plugin->customize_snapshot_manager ); - ob_start(); - $post_type_obj->snapshot_merge_print_script(); - $script_content = ob_get_clean(); - - $this->assertContains( 'select[name="action"]', $script_content ); - $this->assertContains( 'select[name="action2"]', $script_content ); - $this->assertContains( 'merge_snapshot', $script_content ); - $this->assertContains( 'text/javascript', $script_content ); - } - - /** - * Test handle_snapshot_bulk_actions_workaround - * - * @see Post_Type_Back_Compat::handle_snapshot_merge_workaround() - */ - public function test_handle_snapshot_bulk_actions_workaround() { - $GLOBALS['hook_suffix'] = 'posts-' . Post_Type_Back_Compat::SLUG; // WPCS: global override ok. - $_POST['action'] = $_REQUEST['action'] = $_GET['action'] = 'merge_snapshot'; - $_POST['post_type'] = $_REQUEST['post_type'] = $_GET['post_type'] = Post_Type_Back_Compat::SLUG; - $_POST['post'] = $_REQUEST['post'] = $_GET['post'] = array( 1, 2 ); - $_POST['_wpnonce'] = $_REQUEST['_wpnonce'] = $_GET['_wpnonce'] = wp_create_nonce( 'bulk-posts' ); - $_POST['_wp_http_referer'] = $_REQUEST['_wp_http_referer'] = $_GET['_wp_http_referer'] = admin_url(); - $post_type_obj = $this->getMockBuilder( 'CustomizeSnapshots\Post_Type_Back_Compat' ) - ->setConstructorArgs( array( $this->plugin->customize_snapshot_manager ) ) - ->setMethods( array( 'handle_snapshot_merge' ) ) - ->getMock(); - $post_type_obj->expects( $this->once() ) - ->method( 'handle_snapshot_merge' ) - ->will( $this->returnValue( null ) ); - $post_type_obj->handle_snapshot_merge_workaround(); - } - - /** - * Tests preservation of the post_name when submitting a snapshot for review. - * - * @see Post_Type_Back_Compat::preserve_post_name_in_insert_data() - */ - public function test_preserve_post_name_in_insert_data() { - $post_type_obj = new Post_Type_Back_Compat( $this->plugin->customize_snapshot_manager ); - $post_type_obj->init(); - - $post_data = array( - 'post_name' => '', - 'post_type' => 'no', - 'post_status' => 'pending', - ); - $original_post_data = array( - 'post_type' => 'no', - 'post_name' => '!original!', - 'post_status' => 'pending', - ); - $filtered_post_data = $post_type_obj->preserve_post_name_in_insert_data( $post_data, $original_post_data ); - $this->assertEquals( $post_data, $filtered_post_data ); - - $post_data['post_type'] = Post_Type_Back_Compat::SLUG; - $original_post_data['post_type'] = Post_Type_Back_Compat::SLUG; - - $filtered_post_data = $post_type_obj->preserve_post_name_in_insert_data( $post_data, $original_post_data ); - $this->assertEquals( $original_post_data['post_name'], $filtered_post_data['post_name'] ); - } - - /** - * Snapshot publish. - * - * @see Post_Type::save() - */ - function test_publish_snapshot() { - $admin_user_id = $this->factory()->user->create( array( - 'role' => 'administrator', - ) ); - wp_set_current_user( $admin_user_id ); - $post_type = get_plugin_instance()->customize_snapshot_manager->post_type; - $post_type->init(); - $tag_line = 'Snapshot blog'; - - $data = array( - 'blogdescription' => array( - 'value' => $tag_line, - ), - 'foo' => array( - 'value' => 'bar', - ), - 'baz' => array( - 'value' => null, - ), - ); - - $validated_content = array( - 'blogdescription' => array( - 'value' => $tag_line, - ), - 'foo' => array( - 'value' => 'bar', - 'publish_error' => 'unrecognized_setting', - ), - 'baz' => array( - 'value' => null, - 'publish_error' => 'null_value', - ), - ); - - /* - * Ensure that directly updating a post succeeds with invalid settings - * works because the post is a draft. Note that if using - * Customize_Snapshot::set() this would fail because it does validation. - */ - $post_id = $post_type->save( array( - 'uuid' => Customize_Snapshot_Manager::generate_uuid(), - 'data' => $data, - 'status' => 'draft', - ) ); - wp_update_post( array( - 'ID' => $post_id, - 'post_status' => 'draft', - ) ); - $content = $post_type->get_post_content( get_post( $post_id ) ); - $this->assertEquals( $data, $content ); - - /* - * Ensure that attempting to publish a snapshot with invalid settings - * will get the publish_errors added as well as kick it back to pending. - */ - remove_all_filters( 'redirect_post_location' ); - $post_id = $post_type->save( array( - 'uuid' => Customize_Snapshot_Manager::generate_uuid(), - 'data' => $data, - 'status' => 'draft', - ) ); - wp_publish_post( $post_id ); - $snapshot_post = get_post( $post_id ); - $content = $post_type->get_post_content( $snapshot_post ); - $this->assertEquals( 'pending', $snapshot_post->post_status ); - $this->assertEquals( $validated_content, $content ); - $this->assertContains( - 'snapshot_error_on_publish=1', - apply_filters( 'redirect_post_location', get_edit_post_link( $snapshot_post->ID ), $snapshot_post->ID ) - ); - - /* - * Remove invalid settings and now attempt publish. - */ - remove_all_filters( 'redirect_post_location' ); - unset( $data['foo'] ); - unset( $data['baz'] ); - $post_id = $post_type->save( array( - 'uuid' => Customize_Snapshot_Manager::generate_uuid(), - 'data' => $data, - 'status' => 'draft', - ) ); - wp_publish_post( $post_id ); - $snapshot_post = get_post( $post_id ); - $content = $post_type->get_post_content( $snapshot_post ); - $this->assertEquals( 'publish', $snapshot_post->post_status ); - $this->assertEquals( $data, $content ); - $this->assertEquals( $tag_line, get_bloginfo( 'description' ) ); - $this->assertNotContains( - 'snapshot_error_on_publish=1', - apply_filters( 'redirect_post_location', get_edit_post_link( $snapshot_post->ID ), $snapshot_post->ID ) - ); - } -} diff --git a/tests/php/test-class-post-type.php b/tests/php/test-class-post-type.php index fa1f66af..8e433c9d 100644 --- a/tests/php/test-class-post-type.php +++ b/tests/php/test-class-post-type.php @@ -40,35 +40,18 @@ function setUp() { parent::setUp(); $GLOBALS['wp_customize'] = null; // WPCS: Global override ok. $this->plugin = get_plugin_instance(); - if ( $this->plugin->compat ) { - $this->post_type_slug = Post_Type_Back_Compat::SLUG; - } else { - $this->post_type_slug = Post_Type::SLUG; - } + $this->post_type_slug = Post_Type::SLUG; } /** * Get plugin instance accoding to WP version. * - * @param Customize_Snapshot_Manager|Customize_Snapshot_Manager_Back_Compat $manager Manager. + * @param Customize_Snapshot_Manager $manager Manager. * - * @return Post_Type|Post_Type_Back_Compat Post type object. + * @return Post_Type Post type object. */ public function get_new_post_type_instance( $manager ) { - if ( $this->plugin->compat ) { - return new Post_Type_Back_Compat( $manager ); - } else { - return new Post_Type( $manager ); - } - } - - /** - * Mark test incomplete as it is only for new versions. - */ - public function mark_incompatible() { - if ( $this->plugin->compat ) { - $this->markTestSkipped( 'This unit-test require WP version 4.7 or up.' ); - } + return new Post_Type( $manager ); } /** @@ -77,7 +60,6 @@ public function mark_incompatible() { * @see Post_Type::init() */ public function test_init() { - $this->mark_incompatible(); $post_type_obj = $this->get_new_post_type_instance( $this->plugin->customize_snapshot_manager ); $this->plugin->customize_snapshot_manager->init(); $post_type_obj->init(); @@ -118,7 +100,6 @@ public function test_hooks() { */ public function test_extend_changeset_post_type_object() { global $_wp_post_type_features; - $this->mark_incompatible(); $post_type_obj = get_post_type_object( Post_Type::SLUG ); $this->assertArrayHasKey( 'revisions', $_wp_post_type_features[ Post_Type::SLUG ] ); $this->assertTrue( $post_type_obj->show_ui ); @@ -175,7 +156,6 @@ function test_force_at_least_one_revision() { * @covers \CustomizeSnapshots\Post_Type::add_admin_menu_item() */ public function test_add_admin_menu_item() { - $this->mark_incompatible(); global $submenu, $menu; $menu = $submenu = array(); // WPCS: global override ok. $admin_user_id = $this->factory()->user->create( array( @@ -482,7 +462,6 @@ function filter_customize_snapshot_value_preview( $preview, $context ) { * Find a snapshot post by UUID. * * @see Post_Type::find_post() - * @see Post_Type_Back_Compat::find_post() */ public function test_find_post() { $post_type = $this->get_new_post_type_instance( $this->plugin->customize_snapshot_manager ); @@ -545,9 +524,7 @@ public function test_get_post_content() { 'status' => 'publish', ) ); $snapshot_post = get_post( $post_id ); - if ( ! $this->plugin->compat ) { - unset( $data['foo']['publish_error'] ); - } + unset( $data['foo']['publish_error'] ); $this->assertEquals( $data, $post_type->get_post_content( $snapshot_post ) ); // Revision. @@ -634,9 +611,7 @@ public function test_save() { ) ); $this->assertInternalType( 'int', $r ); $expected = $data; - if ( ! $this->plugin->compat ) { - unset( $expected['foo']['publish_error'] ); - } + unset( $expected['foo']['publish_error'] ); $this->assertEquals( $expected, $post_type->get_post_content( get_post( $r ) ) ); $this->assertEquals( get_stylesheet(), get_post_meta( $r, '_snapshot_theme', true ) ); @@ -954,7 +929,6 @@ public function test_filter_out_settings_if_removed_in_metabox() { * @covers \CustomizeSnapshots\Post_Type::remap_customize_meta_cap() */ public function test_remap_customize_meta_cap() { - $this->mark_incompatible(); $this->markTestIncomplete(); } } diff --git a/tests/php/test-class-snapshot-ajax.php b/tests/php/test-class-snapshot-ajax.php deleted file mode 100644 index 64b16546..00000000 --- a/tests/php/test-class-snapshot-ajax.php +++ /dev/null @@ -1,83 +0,0 @@ -plugin = get_plugin_instance(); - if ( $this->plugin->compat ) { - $this->markTestSkipped( 'This unit-test require WP version 4.7 or up.' ); - } - parent::setUp(); - } - /** - * Test handle_migrate_changeset_request. - * - * @see Migrate::handle_migrate_changeset_request() - */ - function test_handle_migrate_changeset_request() { - remove_all_actions( 'wp_ajax_customize_snapshot_migration' ); - delete_option( Migrate::KEY ); - $migrate_obj = $this->getMockBuilder( 'CustomizeSnapshots\Migrate' ) - ->setMethods( array( 'changeset_migrate' ) ) - ->setConstructorArgs( array( $this->plugin ) ) - ->getMock(); - $migrate_obj->expects( $this->any() ) - ->method( 'changeset_migrate' ) - ->will( $this->returnValue( 92 ) ); - $migrate_obj->maybe_migrate(); - $this->set_input_vars(array( - 'nonce' => wp_create_nonce( 'customize-snapshot-migration' ), - 'limit' => 1, - )); - $this->make_ajax_call( 'customize_snapshot_migration' ); - $response = json_decode( $this->_last_response, true ); - $this->assertTrue( $response['success'] ); - $this->assertArrayHasKey( 'remaining_posts', $response['data'] ); - $this->assertEquals( 91, $response['data']['remaining_posts'] ); - } - - /** - * Helper to keep it DRY - * - * @param string $action Action. - */ - protected function make_ajax_call( $action ) { - try { - $this->_handleAjax( $action ); - } catch ( \WPAjaxDieContinueException $e ) { - unset( $e ); - } - } - - /** - * Set input vars. - * - * @param array $vars Input vars. - * @param string $method Request method. - */ - public function set_input_vars( array $vars = array(), $method = 'POST' ) { - $_GET = $_POST = $_REQUEST = wp_slash( $vars ); - $_SERVER['REQUEST_METHOD'] = $method; - } - -} diff --git a/tests/php/test-class-snapshot-rest-api-controller.php b/tests/php/test-class-snapshot-rest-api-controller.php index 0b51c1e7..1f92fd24 100644 --- a/tests/php/test-class-snapshot-rest-api-controller.php +++ b/tests/php/test-class-snapshot-rest-api-controller.php @@ -47,11 +47,7 @@ function setUp() { parent::setUp(); $this->plugin = get_plugin_instance(); - if ( $this->plugin->compat ) { - $this->end_point = 'customize_snapshots'; - } else { - $this->end_point = 'customize_changesets'; - } + $this->end_point = 'customize_changesets'; $this->plugin->customize_snapshot_manager->post_type->init(); diff --git a/tests/test-customize-snapshots.php b/tests/test-customize-snapshots.php index cbbfb1a0..0ce25928 100644 --- a/tests/test-customize-snapshots.php +++ b/tests/test-customize-snapshots.php @@ -24,11 +24,7 @@ class Test_Customize_Snapshots extends \WP_UnitTestCase { */ function setUp() { $plugin = get_plugin_instance(); - if ( $plugin->compat ) { - $this->front_param = 'customize_snapshot_uuid'; - } else { - $this->front_param = 'changeset_uuid'; - } + $this->front_param = 'changeset_uuid'; } /** @@ -63,31 +59,16 @@ function test_customize_snapshots_php_version_text() { $this->assertContains( 'Customize Snapshots plugin error:', customize_snapshots_php_version_text() ); } - /** - * Tests is_previewing_settings(). - * - * @see is_previewing_settings() - */ - public function test_is_previewing_settings() { - if ( ! get_plugin_instance()->compat ) { - $this->markTestIncomplete( 'WordPress Version 4.6.x or below is required for this test-case.' ); - } - $this->assertFalse( is_previewing_settings() ); - do_action( 'customize_preview_init' ); - $this->assertTrue( is_previewing_settings() ); - } - /** * Tests current_snapshot_uuid(). * * @see current_snapshot_uuid() */ public function test_current_snapshot_uuid() { - global $customize_snapshots_plugin; - $uuid = '65aee1ff-af47-47df-9e14-9c69b3017cd3'; - $_REQUEST[ $this->front_param ] = $uuid; - $customize_snapshots_plugin = new Plugin(); - $customize_snapshots_plugin->init(); - $this->assertEquals( $uuid, current_snapshot_uuid() ); + global $wp_customize; + require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); + $changeset_uuid = '65aee1ff-af47-47df-9e14-9c69b3017cd3'; + $wp_customize = new \WP_Customize_Manager( compact( 'changeset_uuid' ) ); + $this->assertEquals( $changeset_uuid, current_snapshot_uuid() ); } } From 922a1ad77835355ebe8c04583bbf0539451e2d8f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 21 Oct 2017 00:51:38 -0700 Subject: [PATCH 35/90] Fix phpcs issues --- php/class-customize-snapshot-manager.php | 16 ++++----- php/class-post-type.php | 2 +- phpcs.xml.dist | 6 ++++ tests/php/test-class-post-type.php | 41 +++++++++++++++++------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index dc8422c3..04514851 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -724,20 +724,20 @@ public function render_templates() { $data = array( 'choices' => array( 'publish' => array( - 'option_text' => __( 'Publish' , 'customize-snapshots' ), - 'alt_text' => __( 'Published' , 'customize-snapshots' ), + 'option_text' => __( 'Publish', 'customize-snapshots' ), + 'alt_text' => __( 'Published', 'customize-snapshots' ), ), 'draft' => array( - 'option_text' => __( 'Save Draft' , 'customize-snapshots' ), - 'alt_text' => __( 'Draft' , 'customize-snapshots' ), + 'option_text' => __( 'Save Draft', 'customize-snapshots' ), + 'alt_text' => __( 'Draft', 'customize-snapshots' ), ), 'future' => array( - 'option_text' => __( 'Schedule' , 'customize-snapshots' ), - 'alt_text' => __( 'Scheduled' , 'customize-snapshots' ), + 'option_text' => __( 'Schedule', 'customize-snapshots' ), + 'alt_text' => __( 'Scheduled', 'customize-snapshots' ), ), 'pending' => array( - 'option_text' => __( 'Save Pending' , 'customize-snapshots' ), - 'alt_text' => __( 'Pending' , 'customize-snapshots' ), + 'option_text' => __( 'Save Pending', 'customize-snapshots' ), + 'alt_text' => __( 'Pending', 'customize-snapshots' ), ), ), 'selected' => 'publish', diff --git a/php/class-post-type.php b/php/class-post-type.php index 24b73fb0..4c3bf21e 100644 --- a/php/class-post-type.php +++ b/php/class-post-type.php @@ -107,7 +107,7 @@ public function init() { add_filter( 'post_link', array( $this, 'filter_post_type_link' ), 10, 2 ); add_action( 'add_meta_boxes_' . static::SLUG, array( $this, 'setup_metaboxes' ), 10, 1 ); - add_action( 'admin_menu',array( $this, 'add_admin_menu_item' ), 99 ); + add_action( 'admin_menu', array( $this, 'add_admin_menu_item' ), 99 ); add_filter( 'map_meta_cap', array( $this, 'remap_customize_meta_cap' ), 5, 4 ); add_filter( 'bulk_actions-edit-' . static::SLUG, array( $this, 'add_snapshot_bulk_actions' ) ); add_filter( 'handle_bulk_actions-edit-' . static::SLUG, array( $this, 'handle_snapshot_merge' ), 10, 3 ); diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 95363929..0f960596 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -12,6 +12,12 @@ 0 + + 0 + + + 0 + 0 diff --git a/tests/php/test-class-post-type.php b/tests/php/test-class-post-type.php index 8e433c9d..a9f1176c 100644 --- a/tests/php/test-class-post-type.php +++ b/tests/php/test-class-post-type.php @@ -261,7 +261,10 @@ public function test_setup_metaboxes() { $post_type = $this->get_new_post_type_instance( $this->plugin->customize_snapshot_manager ); $post_type->init(); - $post_id = $this->factory()->post->create( array( 'post_type' => $this->post_type_slug, 'post_status' => 'draft' ) ); + $post_id = $this->factory()->post->create( array( + 'post_type' => $this->post_type_slug, + 'post_status' => 'draft', + ) ); $wp_meta_boxes = array(); // WPCS: global override ok. $metabox_id = $this->post_type_slug; @@ -542,7 +545,10 @@ public function test_get_post_content() { $this->assertEquals( 'baz', $content['foo']['value'] ); // Bad post data. - $bad_post_id = $this->factory()->post->create( array( 'post_type' => $this->post_type_slug, 'post_content' => 'BADJSON' ) ); + $bad_post_id = $this->factory()->post->create( array( + 'post_type' => $this->post_type_slug, + 'post_content' => 'BADJSON', + ) ); $bad_post = get_post( $bad_post_id ); $content = $post_type->get_post_content( $bad_post ); $this->assertEquals( array(), $content ); @@ -566,17 +572,28 @@ public function test_save() { $this->assertInstanceOf( 'WP_Error', $r ); $this->assertEquals( 'missing_valid_uuid', $r->get_error_code() ); - $r = $post_type->save( array( 'uuid' => self::UUID, 'data' => 'bad' ) ); + $r = $post_type->save( array( + 'uuid' => self::UUID, + 'data' => 'bad', + ) ); $this->assertInstanceOf( 'WP_Error', $r ); $this->assertEquals( 'missing_data', $r->get_error_code() ); // Error: bad_setting_params. - $r = $post_type->save( array( 'uuid' => self::UUID, 'data' => array( 'foo' => 'bar' ) ) ); + $r = $post_type->save( array( + 'uuid' => self::UUID, + 'data' => array( 'foo' => 'bar' ), + ) ); $this->assertInstanceOf( 'WP_Error', $r ); $this->assertEquals( 'bad_setting_params', $r->get_error_code() ); // Error: missing_value_param. - $r = $post_type->save( array( 'uuid' => self::UUID, 'data' => array( 'foo' => array( 'bar' => 'quux' ) ) ) ); + $r = $post_type->save( array( + 'uuid' => self::UUID, + 'data' => array( + 'foo' => array( 'bar' => 'quux' ), + ), + ) ); $this->assertInstanceOf( 'WP_Error', $r ); $this->assertEquals( 'missing_value_param', $r->get_error_code() ); @@ -779,12 +796,14 @@ public function test_add_snapshot_bulk_actions() { public function test_handle_snapshot_merge() { $ids = $this->factory()->post->create_many( 2 ); $posts = array_map( 'get_post', $ids ); - $post_type_obj = $this->getMockBuilder( 'CustomizeSnapshots\Post_Type' ) - ->setConstructorArgs( array( $this->plugin->customize_snapshot_manager ) ) - ->setMethods( array( 'merge_snapshots' ) ) - ->getMock(); - $post_type_obj->expects( $this->once() ) - ->method( 'merge_snapshots' ) + $post_type_obj = $this + ->getMockBuilder( 'CustomizeSnapshots\Post_Type' ) + ->setConstructorArgs( array( $this->plugin->customize_snapshot_manager ) ) + ->setMethods( array( 'merge_snapshots' ) ) + ->getMock(); + $post_type_obj + ->expects( $this->once() ) + ->method( 'merge_snapshots' ) ->with( $posts ) ->will( $this->returnValue( null ) ); $post_type_obj->handle_snapshot_merge( '', 'merge_snapshot', $ids ); From 081e066640ef680234e17c4267bbadeea54c51f6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 22 Oct 2017 17:52:12 -0700 Subject: [PATCH 36/90] Update wp-dev-lib d8540c1...52874e4: Merge pull request xwp/wp-dev-lib#259 from branch fix/jshintignore-absent https://github.com/xwp/wp-dev-lib/compare/d8540c1...52874e4 --- dev-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-lib b/dev-lib index d8540c1f..52874e44 160000 --- a/dev-lib +++ b/dev-lib @@ -1 +1 @@ -Subproject commit d8540c1f036c583f48af5f63c3f6d04db36c0db3 +Subproject commit 52874e44d9e737dfc8f1e9ca33381f45deb9b636 From e8969e1c20027cb54f9f80e78ac70be85c03aa6b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 22 Oct 2017 18:05:45 -0700 Subject: [PATCH 37/90] Skip phpcs on PHP 5.2 and 5.3 --- .dev-lib | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.dev-lib b/.dev-lib index 76f633ce..e65f5ff9 100644 --- a/.dev-lib +++ b/.dev-lib @@ -2,6 +2,10 @@ PATH_INCLUDES='*.* php js css tests' WPCS_GIT_TREE=develop ASSETS_DIR=wp-assets +if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.2" ]] || [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then + DEV_LIB_SKIP="$DEV_LIB_SKIP,phpcs" +fi + function after_wp_install { echo "Installing REST API..." svn export -q https://plugins.svn.wordpress.org/rest-api/trunk/ "$WP_CORE_DIR/src/wp-content/plugins/rest-api" From f29123aebf7ae29cd9e458a99fd121ab20de3c9c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 30 Oct 2017 10:22:03 -0700 Subject: [PATCH 38/90] Move pre-4.9 JS and CSS to compat files --- css/customize-snapshots-compat.css | 350 +++++++ css/customize-snapshots.css | 351 +------ js/customize-snapshots-compat.js | 1223 ++++++++++++++++++++++ js/customize-snapshots.js | 1211 +-------------------- php/class-customize-snapshot-manager.php | 5 +- php/class-plugin.php | 12 +- 6 files changed, 1594 insertions(+), 1558 deletions(-) create mode 100644 css/customize-snapshots-compat.css create mode 100644 js/customize-snapshots-compat.js diff --git a/css/customize-snapshots-compat.css b/css/customize-snapshots-compat.css new file mode 100644 index 00000000..e98c6f3e --- /dev/null +++ b/css/customize-snapshots-compat.css @@ -0,0 +1,350 @@ +#snapshot-preview-link, +#snapshot-expand-button { + float: right; + margin-top: 13px; + margin-right: 4px; + color: #656a6f; +} + +#snapshot-expand-button { + display: block; +} + +#snapshot-expand-button:hover, +#snapshot-expand-button:focus, +#snapshot-expand-button:active { + color: #191e23; +} + +#snapshot-preview-link:hover, +#snapshot-preview-link:focus, +#snapshot-preview-link:active { + color: #191e23; +} + +#snapshot-save { + float: right; + margin-top: 9px; + margin-right: 9px; +} + +#customize-header-actions:not(.button-added) .button#save { + visibility: hidden; +} + +#snapshot-dialog-form h4 { + font-weight: bold; + font-size: 14px; + line-height: 1.5em; + margin: 0 0 .65em; + padding: .25em 0 0; +} + +#snapshot-dialog-form p, +#snapshot-dialog-link a, +#snapshot-dialog-error p { + margin-top: .5em; +} + +#snapshot-dialog-form label { + padding-bottom: 1em; +} + +#snapshot-dialog-link { + word-wrap: break-word; +} + +.wp-customizer.js .ui-dialog { + z-index: 999999; +} + +@media screen and (max-width: 640px) { + #snapshot-save { + margin-top: 6px; + margin-right: 6px; + } +} + +#customize-snapshot { + background: #fff !important; + border-bottom: 1px solid #ddd; + line-height: 1.5; + left: 0; + top: 46px; + position: absolute; + width: 100%; + box-shadow: 0 5px 0 0 rgba(0, 0, 0, 0.05); + padding: 10px; + box-sizing: border-box; +} + +#customize-snapshot .snapshot-schedule-title { + color: #555; +} + +#customize-snapshot .snapshot-schedule-title h3 { + margin: .2em 2em .75em 0; +} + +#customize-snapshot .snapshot-schedule-description { + margin-bottom: 0.5em; + margin-top: 0; +} + +#customize-snapshot .timezone-info { + margin-top: 0.5em; + font-size: smaller; +} + +#customize-snapshot a.snapshot-edit-link { + position: absolute; + top: 4px; + right: 1px; + width: 20px; + height: 20px; + cursor: pointer; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-appearance: none; + background: transparent; + color: #555; + border: none; + padding: 10px; +} + +#customize-snapshot a.snapshot-edit-link:focus, +#customize-snapshot a.snapshot-edit-link:hover { + color: #0073aa; +} + +#customize-snapshot a.snapshot-edit-link:before { + padding: 4px; + position: absolute; + top: 5px; + left: 6px; + -webkit-border-radius: 100%; + border-radius: 100%; +} + +#customize-snapshot a.snapshot-edit-link:focus:before { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +#customize-snapshot .accordion-section-title { + padding: 10px; +} + +#customize-snapshot .reset-time { + font-weight: normal; + font-size: 80%; + display: none; +} + +.snapshot-schedule-control.date-inputs { + display: flex; + display: -ms-flexbox; + align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + justify-content: flex-start; + -webkit-box-pack: start; + -ms-flex-pack: start; +} + +.snapshot-schedule-control input.date-input { + -moz-appearance: textfield; + -webkit-appearance: textfield; + padding-left: 5px; /* 10px padding is not ideal for such small inputs */ + padding-right: 5px; +} + +.snapshot-schedule-control label { + display: inline-block; /* To align items when flex is not supported */ +} + +.snapshot-schedule-control .year.date-input { + min-width: 4em; +} + +.snapshot-schedule-control input[type="number"] { + width: 100%; +} + +.snapshot-schedule-control .time-special-char { + padding-left: 2px; + padding-right: 2px; +} + +.snapshot-schedule-control select { + height: 28px; +} + +@media screen and ( max-width: 782px ) { + .snapshot-schedule-control select { + height: 40px; + } +} + +.snapshot-schedule-control input.date-input::-webkit-outer-spin-button, +.snapshot-schedule-control input.date-input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.wp-full-overlay .wp-full-overlay-sidebar .wp-full-overlay-header { + padding-left: 15px; + z-index: 500101; +} + +.select2-container { + z-index: 500100 !important; +} + +.snapshot-control input[type="text"] { + width: 100%; + line-height: 18px; + margin: 0; +} + +#customize-snapshot .snapshot-schedule-title h3 { + font-size: 16px; +} + +#customize-snapshot .snapshot-controls { + list-style: none; + margin: 0; +} + +.snapshot-future-date-notification.notice { + margin-bottom: 5px; + border-top: 1px solid #eee; + padding: 5px; +} + +/*Status Button*/ +/** @todo Need to add snapshot specific class for all ui classes. **/ + +#snapshot-status-button-wrapper { + float: right; + position: relative; + margin-top: 9px; + height: 28px; +} + +#snapshot-status-button-wrapper .button-primary { + margin-top: 0; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} + +.snapshot-status-button-wrapper .button:active{ + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + -o-transform: none; + transform: none; /* 4.7 added translateY(1px) */ +} + +.ui-selectmenu-menu { + padding: 0; + margin: 0; + position: absolute; + top: 0; + left: 0; + display: none; +} + +.ui-selectmenu-menu .ui-menu { + overflow: auto; + margin: 0; + margin-top: 2px; + overflow-x: hidden; + background: #FFFFFF; + border-top: 0; + border-radius: 0 0 5px 5px; + box-shadow: 0 1px 4px 0 rgba(33, 38, 34, .12), 0 2px 11px 0 rgba(30, 36, 37, .14) +} + +.ui-selectmenu-menu .ui-menu li { + padding: 5px 10px 5px 5px; + margin: 0; +} + +.ui-selectmenu-menu .ui-menu .ui-state-focus { + background: #0073aa; + color: #FFFFFF; + cursor: pointer; +} + +.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { + font-size: 1em; + font-weight: bold; + line-height: 1.5; + padding: 2px 0.4em; + margin: 0.5em 0 0 0; + height: auto; + border: 0; +} + +.ui-selectmenu-open { + display: block; +} + +#snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons { + text-indent: 0; + float: right; + border-radius: 0 3px 3px 0; + padding-left: 0; + padding-right: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + font-size: 20px; + width: 20px; +} + +#snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons:before { + display: inline-block; + margin-left: -3px; +} + +@media screen and ( max-width: 640px ) { + #snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons { + font-size: 17px; + } +} + +.ui-selectmenu-button span.ui-selectmenu-text { + border-radius: 3px 0 0 3px; + opacity: 0; +} + +.ui-selectmenu-menu.ui-front { + z-index: 999999; +} + +.snapshot-status-button-overlay.button { + width: calc( 100% - 20px ); + height: 28px; + position: absolute; + top: 0; + left: 0; + z-index: 1; + border-radius: 3px 0 0 3px; + box-shadow: none; +} + +#customize-header-actions .ui-selectmenu-button { + display: inline-block; + outline-width: 2px; + outline-offset: -2px; +} + diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css index e98c6f3e..b676616c 100644 --- a/css/customize-snapshots.css +++ b/css/customize-snapshots.css @@ -1,350 +1 @@ -#snapshot-preview-link, -#snapshot-expand-button { - float: right; - margin-top: 13px; - margin-right: 4px; - color: #656a6f; -} - -#snapshot-expand-button { - display: block; -} - -#snapshot-expand-button:hover, -#snapshot-expand-button:focus, -#snapshot-expand-button:active { - color: #191e23; -} - -#snapshot-preview-link:hover, -#snapshot-preview-link:focus, -#snapshot-preview-link:active { - color: #191e23; -} - -#snapshot-save { - float: right; - margin-top: 9px; - margin-right: 9px; -} - -#customize-header-actions:not(.button-added) .button#save { - visibility: hidden; -} - -#snapshot-dialog-form h4 { - font-weight: bold; - font-size: 14px; - line-height: 1.5em; - margin: 0 0 .65em; - padding: .25em 0 0; -} - -#snapshot-dialog-form p, -#snapshot-dialog-link a, -#snapshot-dialog-error p { - margin-top: .5em; -} - -#snapshot-dialog-form label { - padding-bottom: 1em; -} - -#snapshot-dialog-link { - word-wrap: break-word; -} - -.wp-customizer.js .ui-dialog { - z-index: 999999; -} - -@media screen and (max-width: 640px) { - #snapshot-save { - margin-top: 6px; - margin-right: 6px; - } -} - -#customize-snapshot { - background: #fff !important; - border-bottom: 1px solid #ddd; - line-height: 1.5; - left: 0; - top: 46px; - position: absolute; - width: 100%; - box-shadow: 0 5px 0 0 rgba(0, 0, 0, 0.05); - padding: 10px; - box-sizing: border-box; -} - -#customize-snapshot .snapshot-schedule-title { - color: #555; -} - -#customize-snapshot .snapshot-schedule-title h3 { - margin: .2em 2em .75em 0; -} - -#customize-snapshot .snapshot-schedule-description { - margin-bottom: 0.5em; - margin-top: 0; -} - -#customize-snapshot .timezone-info { - margin-top: 0.5em; - font-size: smaller; -} - -#customize-snapshot a.snapshot-edit-link { - position: absolute; - top: 4px; - right: 1px; - width: 20px; - height: 20px; - cursor: pointer; - -webkit-box-shadow: none; - box-shadow: none; - -webkit-appearance: none; - background: transparent; - color: #555; - border: none; - padding: 10px; -} - -#customize-snapshot a.snapshot-edit-link:focus, -#customize-snapshot a.snapshot-edit-link:hover { - color: #0073aa; -} - -#customize-snapshot a.snapshot-edit-link:before { - padding: 4px; - position: absolute; - top: 5px; - left: 6px; - -webkit-border-radius: 100%; - border-radius: 100%; -} - -#customize-snapshot a.snapshot-edit-link:focus:before { - -webkit-box-shadow: - 0 0 0 1px #5b9dd9, - 0 0 2px 1px rgba(30, 140, 190, .8); - box-shadow: - 0 0 0 1px #5b9dd9, - 0 0 2px 1px rgba(30, 140, 190, .8); -} - -#customize-snapshot .accordion-section-title { - padding: 10px; -} - -#customize-snapshot .reset-time { - font-weight: normal; - font-size: 80%; - display: none; -} - -.snapshot-schedule-control.date-inputs { - display: flex; - display: -ms-flexbox; - align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - justify-content: flex-start; - -webkit-box-pack: start; - -ms-flex-pack: start; -} - -.snapshot-schedule-control input.date-input { - -moz-appearance: textfield; - -webkit-appearance: textfield; - padding-left: 5px; /* 10px padding is not ideal for such small inputs */ - padding-right: 5px; -} - -.snapshot-schedule-control label { - display: inline-block; /* To align items when flex is not supported */ -} - -.snapshot-schedule-control .year.date-input { - min-width: 4em; -} - -.snapshot-schedule-control input[type="number"] { - width: 100%; -} - -.snapshot-schedule-control .time-special-char { - padding-left: 2px; - padding-right: 2px; -} - -.snapshot-schedule-control select { - height: 28px; -} - -@media screen and ( max-width: 782px ) { - .snapshot-schedule-control select { - height: 40px; - } -} - -.snapshot-schedule-control input.date-input::-webkit-outer-spin-button, -.snapshot-schedule-control input.date-input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; -} - -.wp-full-overlay .wp-full-overlay-sidebar .wp-full-overlay-header { - padding-left: 15px; - z-index: 500101; -} - -.select2-container { - z-index: 500100 !important; -} - -.snapshot-control input[type="text"] { - width: 100%; - line-height: 18px; - margin: 0; -} - -#customize-snapshot .snapshot-schedule-title h3 { - font-size: 16px; -} - -#customize-snapshot .snapshot-controls { - list-style: none; - margin: 0; -} - -.snapshot-future-date-notification.notice { - margin-bottom: 5px; - border-top: 1px solid #eee; - padding: 5px; -} - -/*Status Button*/ -/** @todo Need to add snapshot specific class for all ui classes. **/ - -#snapshot-status-button-wrapper { - float: right; - position: relative; - margin-top: 9px; - height: 28px; -} - -#snapshot-status-button-wrapper .button-primary { - margin-top: 0; - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; -} - -.snapshot-status-button-wrapper .button:active{ - -webkit-transform: none; - -moz-transform: none; - -ms-transform: none; - -o-transform: none; - transform: none; /* 4.7 added translateY(1px) */ -} - -.ui-selectmenu-menu { - padding: 0; - margin: 0; - position: absolute; - top: 0; - left: 0; - display: none; -} - -.ui-selectmenu-menu .ui-menu { - overflow: auto; - margin: 0; - margin-top: 2px; - overflow-x: hidden; - background: #FFFFFF; - border-top: 0; - border-radius: 0 0 5px 5px; - box-shadow: 0 1px 4px 0 rgba(33, 38, 34, .12), 0 2px 11px 0 rgba(30, 36, 37, .14) -} - -.ui-selectmenu-menu .ui-menu li { - padding: 5px 10px 5px 5px; - margin: 0; -} - -.ui-selectmenu-menu .ui-menu .ui-state-focus { - background: #0073aa; - color: #FFFFFF; - cursor: pointer; -} - -.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { - font-size: 1em; - font-weight: bold; - line-height: 1.5; - padding: 2px 0.4em; - margin: 0.5em 0 0 0; - height: auto; - border: 0; -} - -.ui-selectmenu-open { - display: block; -} - -#snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons { - text-indent: 0; - float: right; - border-radius: 0 3px 3px 0; - padding-left: 0; - padding-right: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - font-size: 20px; - width: 20px; -} - -#snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons:before { - display: inline-block; - margin-left: -3px; -} - -@media screen and ( max-width: 640px ) { - #snapshot-status-button-wrapper .ui-selectmenu-button span.dashicons { - font-size: 17px; - } -} - -.ui-selectmenu-button span.ui-selectmenu-text { - border-radius: 3px 0 0 3px; - opacity: 0; -} - -.ui-selectmenu-menu.ui-front { - z-index: 999999; -} - -.snapshot-status-button-overlay.button { - width: calc( 100% - 20px ); - height: 28px; - position: absolute; - top: 0; - left: 0; - z-index: 1; - border-radius: 3px 0 0 3px; - box-shadow: none; -} - -#customize-header-actions .ui-selectmenu-button { - display: inline-block; - outline-width: 2px; - outline-offset: -2px; -} - +/* @todo Add any required styles for 4.9 here. */ diff --git a/js/customize-snapshots-compat.js b/js/customize-snapshots-compat.js new file mode 100644 index 00000000..ea903cdb --- /dev/null +++ b/js/customize-snapshots-compat.js @@ -0,0 +1,1223 @@ +/* global jQuery, wp, JSON */ +/* eslint no-magic-numbers: [ "error", { "ignore": [0,1,-1] } ], consistent-this: [ "error", "snapshot" ] */ + +(function( api, $ ) { + 'use strict'; + + var escKeyCode = 27; + + api.Snapshots = api.Class.extend( { + + data: { + action: '', + uuid: '', + editLink: '', + title: '', + publishDate: '', + postStatus: '', + currentUserCanPublish: true, + initialServerDate: '', + initialServerTimestamp: 0, + initialClientTimestamp: 0, + previewingTheme: '', + i18n: {}, + dirty: false + }, + + uuidParam: 'customize_changeset_uuid', + + initialize: function initialize( snapshotsConfig ) { + var snapshot = this; + + snapshot.schedule = {}; + + if ( _.isObject( snapshotsConfig ) ) { + _.extend( snapshot.data, snapshotsConfig ); + } + + // Set the initial client timestamp. + snapshot.data.initialClientTimestamp = snapshot.dateValueOf(); + + api.bind( 'ready', function() { + api.state.create( 'snapshot-exists', false ); + api.state.create( 'snapshot-saved', true ); + api.state.create( 'snapshot-submitted', true ); + + snapshot.data.uuid = snapshot.data.uuid || api.settings.changeset.uuid; + snapshot.data.title = snapshot.data.title || snapshot.data.uuid; + + snapshot.editBoxAutoSaveTriggered = false; + + if ( api.state.has( 'changesetStatus' ) && api.state( 'changesetStatus' ).get() ) { + api.state( 'snapshot-exists' ).set( true ); + } + + snapshot.extendPreviewerQuery(); + + snapshot.editControlSettings = new api.Values(); + snapshot.editControlSettings.create( 'title', snapshot.data.title ); + snapshot.editControlSettings.create( 'date', snapshot.data.publishDate ); + + api.bind( 'change', function() { + api.state( 'snapshot-submitted' ).set( false ); + } ); + + snapshot.frontendPreviewUrl = new api.Value( api.previewer.previewUrl.get() ); + snapshot.frontendPreviewUrl.link( api.previewer.previewUrl ); + snapshot.isNotSavedPreviewingTheme = false; + + snapshot.addButtons(); + snapshot.editSnapshotUI(); + snapshot.prefilterAjax(); + + api.trigger( 'snapshots-ready', snapshot ); + } ); + + api.bind( 'save', function( request ) { + + request.fail( function( response ) { + var id = '#snapshot-dialog-error', + snapshotDialogPublishError = wp.template( 'snapshot-dialog-error' ); + + if ( response.responseText ) { + + // Insert the dialog error template. + if ( 0 === $( id ).length ) { + $( 'body' ).append( snapshotDialogPublishError( { + title: snapshot.data.i18n.publish, + message: api.state( 'snapshot-exists' ).get() ? snapshot.data.i18n.permsMsg.update : snapshot.data.i18n.permsMsg.save + } ) ); + } + + snapshot.spinner.removeClass( 'is-active' ); + + $( id ).dialog( { + autoOpen: true, + modal: true + } ); + } + } ); + + return request; + } ); + }, + + /** + * Get state query vars. + * + * @return {{}} Query vars for scroll, device, url, and autofocus. + */ + getStateQueryVars: function() { + var snapshot = this, queryVars; + + queryVars = { + 'autofocus[control]': null, + 'autofocus[section]': null, + 'autofocus[panel]': null + }; + + queryVars.scroll = parseInt( api.previewer.scroll, 10 ) || 0; + queryVars.device = api.previewedDevice.get(); + queryVars.url = api.previewer.previewUrl.get(); + + if ( ! api.state( 'activated' ).get() || snapshot.isNotSavedPreviewingTheme ) { + queryVars.previewing_theme = true; + } + + _.find( [ 'control', 'section', 'panel' ], function( constructType ) { + var found = false; + api[ constructType ].each( function( construct ) { // @todo Core needs to support more Backbone methods on wp.customize.Values(). + if ( ! found && construct.expanded && construct.expanded.get() ) { + queryVars[ 'autofocus[' + constructType + ']' ] = construct.id; + found = true; + } + } ); + return found; + } ); + + return queryVars; + }, + + /** + * Update snapshot. + * + * @param {string} status post status. + * @returns {jQuery.promise} Request or promise. + */ + updateSnapshot: function updateSnapshot( status ) { + var snapshot = this, inputDate, + deferred = new $.Deferred(), + request, + requestData = { + status: status + }; + + if ( snapshot.statusButton && snapshot.statusButton.needConfirm ) { + snapshot.statusButton.disbleButton.set( false ); + snapshot.statusButton.updateButtonText( 'confirm-text' ); + snapshot.statusButton.needConfirm = false; + return deferred.promise(); + } + + if ( snapshot.snapshotTitle && snapshot.snapshotTitle.val() && 'publish' !== status ) { + requestData.title = snapshot.editControlSettings( 'title' ).get(); + } + + if ( ! _.isEmpty( snapshot.editContainer ) && snapshot.isFutureDate() && 'publish' !== status ) { + inputDate = snapshot.getDateFromInputs(); + requestData.date = snapshot.formatDate( inputDate ); + } + + if ( 'future' === status ) { + if ( requestData.date ) { + request = snapshot.sendUpdateSnapshotRequest( requestData ); + } + } else { + request = snapshot.sendUpdateSnapshotRequest( requestData ); + } + + return request ? request : deferred.promise(); + }, + + /** + * Make the AJAX request to update/save a snapshot. + * + * @param {object} options Options. + * @param {string} options.status The post status for the snapshot. + * @return {object} request. + */ + sendUpdateSnapshotRequest: function sendUpdateSnapshotRequest( options ) { + var snapshot = this, + request, data, isPublishStatus; + + data = _.extend( + { + status: 'draft' + }, + options + ); + + api.state( 'snapshot-saved' ).set( false ); + snapshot.statusButton.disable( true ); + snapshot.spinner.addClass( 'is-active' ); + + request = api.previewer.save( data ); + + isPublishStatus = 'publish' === data.status; + + request.always( function( response ) { + snapshot.spinner.removeClass( 'is-active' ); + if ( response.edit_link ) { + snapshot.data.editLink = response.edit_link; + } + if ( response.publish_date ) { + snapshot.data.publishDate = response.publish_date; + } + if ( response.title ) { + snapshot.data.title = response.title; + } + + snapshot.data.dirty = false; + } ); + + request.done( function( response ) { + var url = api.previewer.previewUrl(), + customizeUrl = window.location.href, + savedDelay = 400; + + /*** + * Delay because api.Posts.updateSettingsQuietly updates the settings after save, which triggers + * api change causing the publish button to get enabled again. + */ + _.delay( function() { + api.state( 'snapshot-saved' ).set( true ); + if ( 'pending' === data.status ) { + api.state( 'snapshot-submitted' ).set( true ); + } + }, savedDelay ); + + api.state( 'snapshot-exists' ).set( ! isPublishStatus ); + + snapshot.statusButton.disableSelect.set( isPublishStatus ); + snapshot.statusButton.disbleButton.set( true ); + snapshot.snapshotExpandButton.toggle( ! isPublishStatus ); + snapshot.previewLink.toggle( ! isPublishStatus ); + + if ( isPublishStatus ) { + snapshot.removeParamFromClose( 'customize_changeset_uuid' ); + } + + snapshot.statusButton.updateButtonText( 'alt-text' ); + + // Trigger an event for plugins to use. + api.trigger( 'customize-snapshots-update', { + previewUrl: url, + customizeUrl: customizeUrl, + uuid: snapshot.data.uuid, + response: response + } ); + } ); + + request.fail( function( response ) { + var id = '#snapshot-dialog-error', + snapshotDialogShareError = wp.template( 'snapshot-dialog-error' ), + messages = snapshot.data.i18n.errorMsg, + invalidityCount = 0, + dialogElement; + + snapshot.statusButton.disableSelect.set( false ); + snapshot.statusButton.disbleButton.set( false ); + + if ( response.setting_validities ) { + invalidityCount = _.size( response.setting_validities, function( validity ) { + return true !== validity; + } ); + } + + /* + * Short-circuit if there are setting validation errors, since the error messages + * will be displayed with the controls themselves. Eventually, once we have + * a global notification area in the Customizer, we can eliminate this + * short-circuit and instead display the messages in there. + * See https://core.trac.wordpress.org/ticket/35210 + */ + if ( invalidityCount > 0 ) { + return; + } + + if ( response.errors ) { + messages += ' ' + _.pluck( response.errors, 'message' ).join( ' ' ); + } + + // Insert the snapshot dialog error template. + dialogElement = $( id ); + if ( ! dialogElement.length ) { + dialogElement = $( snapshotDialogShareError( { + title: snapshot.data.i18n.errorTitle, + message: messages + } ) ); + $( 'body' ).append( dialogElement ); + } + + // Open the dialog. + $( id ).dialog( { + autoOpen: true, + modal: true + } ); + } ); + + return request; + }, + + /** + * Create the snapshot buttons. + * + * @return {void} + */ + addButtons: function addButtons() { + var snapshot = this, disableButton = true, disableSelectButton = true, + setPreviewLinkHref, currentTheme, savedPreviewingTheme, themeNotActiveOrSaved; + + snapshot.spinner = $( '#customize-header-actions' ).find( '.spinner' ); + snapshot.publishButton = $( '#save' ); + + snapshot.publishButton.addClass( 'hidden' ); + snapshot.statusButton = snapshot.addStatusButton(); + + if ( api.state( 'changesetStatus' ).get() ) { + disableSelectButton = false; + if ( 'auto-draft' === api.state( 'changesetStatus' ).get() ) { + disableButton = false; + } else { + snapshot.statusButton.updateButtonText( 'alt-text' ); + } + } + + currentTheme = api.settings.theme.stylesheet; // Or previewing theme. + savedPreviewingTheme = snapshot.data.previewingTheme; + themeNotActiveOrSaved = ! api.state( 'activated' ).get() && ! savedPreviewingTheme; + snapshot.isNotSavedPreviewingTheme = savedPreviewingTheme && savedPreviewingTheme !== currentTheme; + + if ( themeNotActiveOrSaved || snapshot.isNotSavedPreviewingTheme ) { + disableButton = false; + disableSelectButton = false; + } + + snapshot.statusButton.disbleButton.set( disableButton ); + snapshot.statusButton.disableSelect.set( disableSelectButton ); + + // Preview link. + snapshot.previewLink = $( $.trim( wp.template( 'snapshot-preview-link' )() ) ); + snapshot.previewLink.toggle( api.state( 'snapshot-saved' ).get() ); + snapshot.previewLink.attr( 'target', snapshot.data.uuid ); + setPreviewLinkHref = _.debounce( function() { + var queryVars; + if ( api.state( 'snapshot-exists' ).get() ) { + snapshot.previewLink.attr( 'href', snapshot.getSnapshotFrontendPreviewUrl() ); + } else { + snapshot.previewLink.attr( 'href', snapshot.frontendPreviewUrl.get() ); + } + + // Add the customize_theme param to the frontend URL if the theme is not active. + if ( ! api.state( 'activated' ).get() ) { + queryVars = snapshot.parseQueryString( snapshot.previewLink.prop( 'search' ).substr( 1 ) ); + queryVars.customize_theme = api.settings.theme.stylesheet; + snapshot.previewLink.prop( 'search', $.param( queryVars ) ); + } + } ); + snapshot.frontendPreviewUrl.bind( setPreviewLinkHref ); + setPreviewLinkHref(); + api.state.bind( 'change', setPreviewLinkHref ); + api.bind( 'saved', setPreviewLinkHref ); + snapshot.statusButton.container.after( snapshot.previewLink ); + api.state( 'snapshot-saved' ).bind( function( saved ) { + snapshot.previewLink.toggle( saved ); + } ); + + // Edit button. + snapshot.snapshotExpandButton = $( $.trim( wp.template( 'snapshot-expand-button' )( {} ) ) ); + snapshot.statusButton.container.after( snapshot.snapshotExpandButton ); + + if ( ! snapshot.data.editLink ) { + snapshot.snapshotExpandButton.hide(); + snapshot.previewLink.hide(); + } + + api.state( 'change', function() { + snapshot.snapshotExpandButton.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() ); + } ); + + api.state( 'snapshot-exists' ).bind( function( exist ) { + snapshot.snapshotExpandButton.toggle( exist ); + snapshot.previewLink.toggle( exist ); + } ); + + api.bind( 'change', function() { + if ( api.state( 'snapshot-saved' ).get() ) { + snapshot.statusButton.disable( false ); + if ( snapshot.statusButton.button.data( 'confirm-text' ) !== snapshot.statusButton.buttonText.get() ) { + snapshot.statusButton.updateButtonText( 'button-text' ); + } + if ( snapshot.submitButton ) { + snapshot.submitButton.prop( 'disabled', false ); + } + if ( snapshot.saveButton ) { + snapshot.saveButton.prop( 'disabled', false ); + } + api.state( 'snapshot-saved' ).set( false ); + } + } ); + + if ( ! snapshot.data.currentUserCanPublish ) { + snapshot.addSubmitButton(); + snapshot.addSaveButton(); + } + }, + + /** + * Adds Submit Button when user does not have 'customize_publish' permission. + * + * @return {void} + */ + addSubmitButton: function() { + var snapshot = this, disableSubmitButton; + + disableSubmitButton = 'pending' === snapshot.data.postStatus || ! api.state( 'snapshot-exists' ).get(); + + if ( snapshot.statusButton ) { + snapshot.statusButton.container.hide(); + } else { + snapshot.publishButton.hide(); + } + + snapshot.submitButton = $( $.trim( wp.template( 'snapshot-submit' )( { + buttonText: snapshot.data.i18n.submit + } ) ) ); + + snapshot.submitButton.prop( 'disabled', disableSubmitButton ); + snapshot.submitButton.insertBefore( snapshot.publishButton ); + api.state( 'snapshot-submitted' ).bind( function( submitted ) { + snapshot.submitButton.prop( 'disabled', submitted ); + } ); + + snapshot.submitButton.on( 'click', function( event ) { + event.preventDefault(); + snapshot.submitButton.prop( 'disabled', true ); + if ( snapshot.saveButton ) { + snapshot.saveButton.prop( 'disabled', true ); + } + snapshot.updateSnapshot( 'pending' ).fail( function() { + snapshot.submitButton.prop( 'disabled', false ); + } ); + } ); + + snapshot.editControlSettings.bind( 'change', function() { + if ( api.state( 'snapshot-saved' ).get() ) { + snapshot.submitButton.prop( 'disabled', false ); + } + } ); + }, + + /** + * Adds Save Button when user does not have 'customize_publish' permission. + * + * @return {void} + */ + addSaveButton: function() { + var snapshot = this, disableSaveButton, isSaved; + + isSaved = _.contains( [ 'future', 'pending', 'draft' ], api.state( 'changesetStatus' ).get() ); + disableSaveButton = isSaved || ! api.state( 'snapshot-exists' ).get(); + + snapshot.saveButton = $( $.trim( wp.template( 'snapshot-save' )( { + buttonText: isSaved ? snapshot.data.i18n.updateButton : snapshot.data.i18n.saveButton + } ) ) ); + + snapshot.saveButton.prop( 'disabled', disableSaveButton ); + snapshot.saveButton.insertBefore( snapshot.publishButton ); + + api.state( 'snapshot-submitted' ).bind( function( submitted ) { + if ( submitted ) { + snapshot.saveButton.prop( 'disabled', true ); + } + } ); + + snapshot.saveButton.on( 'click', function( event ) { + event.preventDefault(); + snapshot.saveButton.prop( 'disabled', true ); + snapshot.submitButton.prop( 'disabled', true ); + snapshot.updateSnapshot( 'draft' ).done( function() { + snapshot.saveButton.prop( 'disabled', true ); + snapshot.submitButton.prop( 'disabled', false ); + snapshot.saveButton.text( snapshot.data.i18n.updateButton ); + } ).fail( function() { + snapshot.saveButton.prop( 'disabled', false ); + snapshot.submitButton.prop( 'disabled', false ); + } ); + } ); + + snapshot.editControlSettings.bind( 'change', function() { + if ( api.state( 'snapshot-saved' ).get() ) { + snapshot.saveButton.prop( 'disabled', false ); + } + } ); + }, + + /** + * Renders snapshot schedule and handles it's events. + * + * @returns {void} + */ + editSnapshotUI: function editSnapshotUI() { + var snapshot = this, sliceBegin = 0, + sliceEnd = -2, updateUI, toggleDateNotification; + + snapshot.snapshotEditContainerDisplayed = new api.Value( false ); + + updateUI = function() { + snapshot.populateSetting(); + }; + + // Inject the UI. + if ( _.isEmpty( snapshot.editContainer ) ) { + if ( '0000-00-00 00:00:00' === snapshot.data.publishDate ) { + snapshot.data.publishDate = snapshot.getCurrentTime(); + } + + // Normalize date with secs set as zeros removed. + snapshot.data.publishDate = snapshot.data.publishDate.slice( sliceBegin, sliceEnd ) + '00'; + + // Extend the snapshots data object and add the parsed datetime strings. + snapshot.data = _.extend( snapshot.data, snapshot.parseDateTime( snapshot.data.publishDate ) ); + + // Add the template to the DOM. + snapshot.editContainer = $( $.trim( wp.template( 'snapshot-edit-container' )( snapshot.data ) ) ); + snapshot.editContainer.hide().appendTo( $( '#customize-header-actions' ) ); + snapshot.dateNotification = snapshot.editContainer.find( '.snapshot-future-date-notification' ); + snapshot.countdown = snapshot.editContainer.find( '.snapshot-scheduled-countdown' ); + snapshot.dateControl = snapshot.editContainer.find( '.snapshot-control-date' ); + + if ( snapshot.data.currentUserCanPublish ) { + + // Store the date inputs. + snapshot.schedule.inputs = snapshot.editContainer.find( '.date-input' ); + + snapshot.schedule.inputs.on( 'input', updateUI ); + + snapshot.schedule.inputs.on( 'blur', function() { + snapshot.populateInputs(); + updateUI(); + } ); + + snapshot.updateCountdown(); + + snapshot.editContainer.find( '.reset-time a' ).on( 'click', function( event ) { + event.preventDefault(); + snapshot.updateSnapshotEditControls(); + } ); + } + + if ( snapshot.statusButton && 'future' !== snapshot.statusButton.value.get() ) { + snapshot.countdown.hide(); + } + + snapshot.snapshotTitle = snapshot.editContainer.find( '#snapshot-title' ); + snapshot.snapshotTitle.on( 'input', updateUI ); + } + + toggleDateNotification = function() { + if ( ! _.isEmpty( snapshot.dateNotification ) ) { + snapshot.dateNotification.toggle( ! snapshot.isFutureDate() ); + } + }; + + // Set up toggling of the schedule container. + snapshot.snapshotEditContainerDisplayed.bind( function( isDisplayed ) { + + if ( snapshot.statusButton ) { + snapshot.dateControl.toggle( 'future' === snapshot.statusButton.value.get() ); + } + + if ( isDisplayed ) { + snapshot.editContainer.stop().slideDown( 'fast' ).attr( 'aria-expanded', 'true' ); + snapshot.snapshotExpandButton.attr( 'aria-pressed', 'true' ); + snapshot.snapshotExpandButton.prop( 'title', snapshot.data.i18n.collapseSnapshotScheduling ); + toggleDateNotification(); + } else { + snapshot.editContainer.stop().slideUp( 'fast' ).attr( 'aria-expanded', 'false' ); + snapshot.snapshotExpandButton.attr( 'aria-pressed', 'false' ); + snapshot.snapshotExpandButton.prop( 'title', snapshot.data.i18n.expandSnapshotScheduling ); + } + } ); + + snapshot.editControlSettings( 'date' ).bind( function() { + toggleDateNotification(); + } ); + + // Toggle schedule container when clicking the button. + snapshot.snapshotExpandButton.on( 'click', function() { + snapshot.snapshotEditContainerDisplayed.set( ! snapshot.snapshotEditContainerDisplayed.get() ); + } ); + + // Collapse the schedule container when Esc is pressed while the button is focused. + snapshot.snapshotExpandButton.on( 'keydown', function( event ) { + if ( escKeyCode === event.which && snapshot.snapshotEditContainerDisplayed.get() ) { + event.stopPropagation(); + event.preventDefault(); + snapshot.snapshotEditContainerDisplayed.set( false ); + } + } ); + + // Collapse the schedule container when Esc is pressed inside of the schedule container. + snapshot.editContainer.on( 'keydown', function( event ) { + if ( escKeyCode === event.which && snapshot.snapshotEditContainerDisplayed.get() ) { + event.stopPropagation(); + event.preventDefault(); + snapshot.snapshotEditContainerDisplayed.set( false ); + snapshot.snapshotExpandButton.focus(); + } + } ); + + // Collapse the schedule container interacting outside the schedule container. + $( 'body' ).on( 'mousedown', function( event ) { + var isDisplayed = snapshot.snapshotEditContainerDisplayed.get(), + isTargetEditContainer = snapshot.editContainer.is( event.target ) || 0 !== snapshot.editContainer.has( event.target ).length, + isTargetExpandButton = snapshot.snapshotExpandButton.is( event.target ); + + if ( isDisplayed && ! isTargetEditContainer && ! isTargetExpandButton ) { + snapshot.snapshotEditContainerDisplayed.set( false ); + } + } ); + + snapshot.snapshotEditContainerDisplayed.set( false ); + + api.state( 'snapshot-saved' ).bind( function( saved ) { + if ( saved && ! snapshot.dirtyEditControlValues ) { + snapshot.updateSnapshotEditControls(); + } + } ); + + api.bind( 'change', function() { + snapshot.data.dirty = true; + snapshot.editContainer.find( 'a.snapshot-edit-link' ).hide(); + } ); + + api.state( 'snapshot-exists' ).bind( function( exists ) { + if ( exists && ! _.isEmpty( snapshot.editContainer ) ) { + snapshot.updateSnapshotEditControls(); + } else { + snapshot.snapshotEditContainerDisplayed.set( false ); + } + } ); + + if ( snapshot.statusButton ) { + snapshot.updateSnapshotEditControls(); + } + + snapshot.autoSaveEditBox(); + }, + + /** + * Auto save the edit box values. + * + * @return {void} + */ + autoSaveEditBox: function() { + var snapshot = this, update, + delay = 2000, status, isFutureDateAndStatus; + + snapshot.updatePending = false; + snapshot.dirtyEditControlValues = false; + + update = _.debounce( function() { + status = snapshot.statusButton.value.get(); + isFutureDateAndStatus = 'future' === status && ! snapshot.isFutureDate(); + if ( 'publish' === status || isFutureDateAndStatus ) { + snapshot.updatePending = false; + return; + } + snapshot.updatePending = true; + snapshot.editBoxAutoSaveTriggered = true; + snapshot.dirtyEditControlValues = false; + snapshot.updateSnapshot( status ).done( function() { + snapshot.updatePending = snapshot.dirtyEditControlValues; + if ( ! snapshot.updatePending ) { + snapshot.updateSnapshotEditControls(); + } else if ( snapshot.dirtyEditControlValues ) { + update(); + } + snapshot.dirtyEditControlValues = false; + } ).fail( function() { + snapshot.updatePending = false; + snapshot.dirtyEditControlValues = true; + } ); + }, delay ); + + snapshot.editControlSettings( 'title' ).bind( function() { + snapshot.dirtyEditControlValues = true; + if ( ! snapshot.updatePending ) { + update(); + } + } ); + + snapshot.editControlSettings( 'date' ).bind( function() { + if ( snapshot.isFutureDate() ) { + snapshot.dirtyEditControlValues = true; + if ( ! snapshot.updatePending ) { + update(); + } + } + } ); + + $( window ).on( 'beforeunload.customize-confirm', function() { + if ( snapshot.updatePending || snapshot.dirtyEditControlValues ) { + return snapshot.data.i18n.aysMsg; + } + return undefined; + } ); + + // @todo Show loader and disable button while auto saving. + + api.bind( 'changeset-saved', function() { + if ( 'auto-draft' !== api.state( 'changesetStatus' ).get() ) { + api.state( 'saved' ).set( true ); // Suppress the AYS dialog. + } + }); + }, + + /** + * Get the preview URL with the snapshot UUID attached. + * + * @returns {string} URL. + */ + getSnapshotFrontendPreviewUrl: function getSnapshotFrontendPreviewUrl() { + var snapshot = this, a = document.createElement( 'a' ); + a.href = snapshot.frontendPreviewUrl.get(); + if ( a.search ) { + a.search += '&'; + } + a.search += snapshot.uuidParam + '=' + snapshot.data.uuid; + return a.href; + }, + + /** + * Updates snapshot schedule with `snapshot.data`. + * + * @return {void} + */ + updateSnapshotEditControls: function updateSnapshotEditControls() { + var snapshot = this, + parsed, + status, + sliceBegin = 0, + sliceEnd = -2; + + if ( _.isEmpty( snapshot.editContainer ) ) { + return; + } + + status = api.state( 'changesetStatus' ).get(); + + if ( snapshot.data.currentUserCanPublish ) { + if ( '0000-00-00 00:00:00' === snapshot.data.publishDate || ! status || 'auto-draft' === status ) { + snapshot.data.publishDate = snapshot.getCurrentTime(); + } + + // Normalize date with seconds removed. + snapshot.data.publishDate = snapshot.data.publishDate.slice( sliceBegin, sliceEnd ) + '00'; + parsed = snapshot.parseDateTime( snapshot.data.publishDate ); + + // Update date controls. + snapshot.schedule.inputs.each( function() { + var input = $( this ), + fieldName = input.data( 'date-input' ); + $( this ).val( parsed[ fieldName ] ); + } ); + } + + snapshot.editContainer.find( 'a.snapshot-edit-link' ) + .attr( 'href', snapshot.data.editLink ) + .show(); + if ( ! _.isEmpty( snapshot.data.title ) ) { + snapshot.snapshotTitle.val( snapshot.data.title ); + } + snapshot.populateSetting(); + }, + + /** + * Update the scheduled countdown text. + * + * Hides countdown if post_status is not already future. + * Toggles the countdown if there is no remaining time. + * + * @returns {boolean} True if date inputs are valid. + */ + updateCountdown: function updateCountdown() { + var snapshot = this, + countdownTemplate = wp.template( 'snapshot-scheduled-countdown' ), + dateTimeFromInput = snapshot.getDateFromInputs(), + millisecondsDivider = 1000, + remainingTime; + + if ( ! dateTimeFromInput ) { + return false; + } + + remainingTime = dateTimeFromInput.valueOf(); + remainingTime -= snapshot.dateValueOf( snapshot.getCurrentTime() ); + remainingTime = Math.ceil( remainingTime / millisecondsDivider ); + + if ( 0 < remainingTime ) { + snapshot.countdown.text( countdownTemplate( { + remainingTime: remainingTime + } ) ); + snapshot.countdown.show(); + } else { + snapshot.countdown.hide(); + } + + return true; + }, + + /** + * Get date from inputs. + * + * @returns {Date|null} Date created from inputs or null if invalid date. + */ + getDateFromInputs: function getDateFromInputs() { + var snapshot = this, + template = snapshot.editContainer, + monthOffset = 1, + date; + + date = new Date( + parseInt( template.find( '[data-date-input="year"]' ).val(), 10 ), + parseInt( template.find( '[data-date-input="month"]' ).val(), 10 ) - monthOffset, + parseInt( template.find( '[data-date-input="day"]' ).val(), 10 ), + parseInt( template.find( '[data-date-input="hour"]' ).val(), 10 ), + parseInt( template.find( '[data-date-input="minute"]' ).val(), 10 ) + ); + + if ( isNaN( date.valueOf() ) ) { + return null; + } + + date.setSeconds( 0 ); + + return date; + }, + + /** + * Parse datetime string. + * + * @param {string} datetime Date/Time string. + * @returns {object|null} Returns object containing date components or null if parse error. + */ + parseDateTime: function parseDateTime( datetime ) { + var matches = datetime.match( /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ ); + + if ( ! matches ) { + return null; + } + + matches.shift(); + + return { + year: matches.shift(), + month: matches.shift(), + day: matches.shift(), + hour: matches.shift(), + minute: matches.shift(), + second: matches.shift() + }; + }, + + /** + * Format a Date Object. Returns 'Y-m-d H:i:s' format. + * + * @props http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript#comment33639551_10073699 + * + * @param {Date} date A Date object. + * @returns {string} A formatted date String. + */ + formatDate: function formatDate( date ) { + var formattedDate, + yearLength = 4, + nonYearLength = 2, + monthOffset = 1; + + formattedDate = ( '0000' + String( date.getFullYear() ) ).substr( -yearLength, yearLength ); + formattedDate += '-' + ( '00' + String( date.getMonth() + monthOffset ) ).substr( -nonYearLength, nonYearLength ); + formattedDate += '-' + ( '00' + String( date.getDate() ) ).substr( -nonYearLength, nonYearLength ); + formattedDate += ' ' + ( '00' + String( date.getHours() ) ).substr( -nonYearLength, nonYearLength ); + formattedDate += ':' + ( '00' + String( date.getMinutes() ) ).substr( -nonYearLength, nonYearLength ); + formattedDate += ':' + ( '00' + String( date.getSeconds() ) ).substr( -nonYearLength, nonYearLength ); + + return formattedDate; + }, + + /** + * Populate inputs from the setting value, if none of them are currently focused. + * + * @returns {boolean} Whether the inputs were populated. + */ + populateInputs: function populateInputs() { + var snapshot = this, parsed; + + if ( snapshot.schedule.inputs.is( ':focus' ) || '0000-00-00 00:00:00' === snapshot.data.publishDate ) { + return false; + } + + parsed = snapshot.parseDateTime( snapshot.data.publishDate ); + if ( ! parsed ) { + return false; + } + + snapshot.schedule.inputs.each( function() { + var input = $( this ), + fieldName = input.data( 'date-input' ); + + if ( ! $( this ).is( 'select' ) && '' === $( this ).val() ) { + $( this ).val( parsed[fieldName] ); + } + } ); + return true; + }, + + /** + * Populate setting value from the inputs. + * + * @returns {void} + */ + populateSetting: function populateSetting() { + var snapshot = this, + date = snapshot.getDateFromInputs(), + scheduled; + + snapshot.editControlSettings( 'title' ).set( snapshot.snapshotTitle.val() ); + + if ( ! date || ! snapshot.data.currentUserCanPublish ) { + return; + } + + date.setSeconds( 0 ); + scheduled = snapshot.formatDate( date ) !== snapshot.data.publishDate; + snapshot.editControlSettings( 'date' ).set( snapshot.formatDate( date ) ); + + if ( 'future' === snapshot.statusButton.value.get() ) { + snapshot.updateCountdown(); + } + + snapshot.editContainer.find( '.reset-time' ).toggle( scheduled ); + }, + + /** + * Check if the schedule date is in the future. + * + * @returns {boolean} True if future date. + */ + isFutureDate: function isFutureDate() { + var snapshot = this, + date = snapshot.getDateFromInputs(), + millisecondsDivider = 1000, + remainingTime; + + if ( ! date ) { + return false; + } + + remainingTime = snapshot.dateValueOf( date ); + remainingTime -= snapshot.dateValueOf( snapshot.getCurrentTime() ); + remainingTime = Math.ceil( remainingTime / millisecondsDivider ); + return 0 < remainingTime; + }, + + /** + * Get current date/time in the site's timezone. + * + * Same functionality as the `current_time( 'mysql', false )` function in PHP. + * + * @returns {string} Current datetime string. + */ + getCurrentTime: function getCurrentTime() { + var snapshot = this, currentDate, currentTimestamp, timestampDifferential; + currentTimestamp = ( new Date() ).valueOf(); + currentDate = snapshot.parsePostDate( snapshot.data.initialServerDate ); + timestampDifferential = currentTimestamp - snapshot.data.initialClientTimestamp; + timestampDifferential += snapshot.data.initialClientTimestamp - snapshot.data.initialServerTimestamp; + currentDate.setTime( currentDate.getTime() + timestampDifferential ); + return snapshot.formatDate( currentDate ); + }, + + /** + * Parse post date string in YYYY-MM-DD HH:MM:SS format (local timezone). + * + * @param {string} postDate Post date string. + * @returns {Date} Parsed date. + */ + parsePostDate: function parsePostDate( postDate ) { + var dateParts = _.map( postDate.split( /\D/ ), function( datePart ) { + return parseInt( datePart, 10 ); + } ); + return new Date( dateParts[0], dateParts[1] - 1, dateParts[2], dateParts[3], dateParts[4], dateParts[5] ); // eslint-disable-line no-magic-numbers + }, + + /** + * Get the primitive value of a Date object. + * + * @param {string|Date} [dateString] The post status for the snapshot. + * @returns {object|string} The primitive value or date object. + */ + dateValueOf: function dateValueOf( dateString ) { + var date; + + if ( 'string' === typeof dateString ) { + date = new Date( dateString ); + } else if ( dateString instanceof Date ) { + date = dateString; + } else { + date = new Date(); + } + + return date.valueOf(); + }, + + /** + * Amend the preview query so we can update the snapshot during `changeset_save`. + * + * @return {void} + */ + extendPreviewerQuery: function extendPreviewerQuery() { + var snapshot = this, originalQuery = api.previewer.query; + + api.previewer.query = function() { + var retval = originalQuery.apply( this, arguments ); + + if ( ! _.contains( [ 'future', 'pending', 'draft' ], snapshot.statusButton.value.get() ) ) { + return retval; + } + + retval.customizer_state_query_vars = JSON.stringify( snapshot.getStateQueryVars() ); + + if ( snapshot.editControlSettings( 'title' ).get() ) { + retval.customize_changeset_title = snapshot.editControlSettings( 'title' ).get(); + } + if ( snapshot.editControlSettings( 'date' ).get() && snapshot.isFutureDate() ) { + retval.customize_changeset_date = snapshot.editControlSettings( 'date' ).get(); + } + return retval; + }; + }, + + /** + * Add status button. + * + * @return {object} status button. + */ + addStatusButton: function addStatusButton() { + var snapshot = this, selectMenuButton, statusButton, selectedOption, buttonText, changesetStatus, selectedStatus; + changesetStatus = api.state( 'changesetStatus' ).get(); + statusButton = {}; + + selectedStatus = changesetStatus && 'auto-draft' !== changesetStatus ? changesetStatus : 'publish'; + + statusButton.value = new api.Value( selectedStatus ); + statusButton.disbleButton = new api.Value(); + statusButton.disableSelect = new api.Value(); + statusButton.buttonText = new api.Value(); + statusButton.needConfirm = false; + + statusButton.container = $( $.trim( wp.template( 'snapshot-status-button' )({ + selected: selectedStatus + }) ) ); + statusButton.button = statusButton.container.find( '.snapshot-status-button-overlay' ); + statusButton.select = statusButton.container.find( 'select' ); + statusButton.select.selectmenu({ + width: 'auto', + icons: { + button: 'dashicons dashicons-arrow-down' + }, + change: function( event, ui ) { + statusButton.value.set( ui.item.value ); + }, + select: function() { + if ( statusButton.hiddenButton ) { + statusButton.hiddenButton.text( statusButton.buttonText.get() ); + } + } + }); + + selectMenuButton = statusButton.container.find( '.ui-selectmenu-button' ); + statusButton.hiddenButton = selectMenuButton.find( '.ui-selectmenu-text' ); + statusButton.hiddenButton.addClass( 'button button-primary' ); + + statusButton.dropDown = selectMenuButton.find( '.ui-icon' ); + statusButton.dropDown.addClass( 'button button-primary' ); + + statusButton.updateButtonText = function( dataAttr ) { + buttonText = statusButton.button.data( dataAttr ); + statusButton.button.text( buttonText ); + statusButton.hiddenButton.text( buttonText ); + statusButton.buttonText.set( buttonText ); + }; + + statusButton.value.bind( function( status ) { + selectedOption = statusButton.select.find( 'option:selected' ); + statusButton.button.data( 'alt-text', selectedOption.data( 'alt-text' ) ); + statusButton.button.data( 'button-text', selectedOption.text() ); + statusButton.updateButtonText( 'button-text' ); + + if ( 'publish' === status ) { + snapshot.snapshotExpandButton.hide(); + statusButton.button.data( 'confirm-text', selectedOption.data( 'confirm-text' ) ); + statusButton.button.data( 'publish-text', selectedOption.data( 'publish-text' ) ); + statusButton.needConfirm = true; + } + + if ( 'future' === status ) { + snapshot.snapshotEditContainerDisplayed.set( true ); + snapshot.snapshotExpandButton.show(); + if ( snapshot.isFutureDate() ) { + snapshot.countdown.show(); + snapshot.updateSnapshot( status ); + } + } else { + snapshot.updateSnapshot( status ); + snapshot.snapshotEditContainerDisplayed.set( false ); + snapshot.countdown.hide(); + } + } ); + + statusButton.disbleButton.bind( function( disabled ) { + statusButton.button.prop( 'disabled', disabled ); + } ); + + statusButton.disableSelect.bind( function( disabled ) { + statusButton.select.selectmenu( disabled ? 'disable' : 'enable' ); + statusButton.dropDown.toggleClass( 'disabled', disabled ); + } ); + + statusButton.disable = function( disable ) { + statusButton.disableSelect.set( disable ); + statusButton.disbleButton.set( disable ); + }; + + statusButton.button.on( 'click', function( event ) { + event.preventDefault(); + snapshot.updateSnapshot( statusButton.value.get() ); + } ); + + snapshot.publishButton.after( statusButton.container ); + + return statusButton; + }, + + /** + * Remove 'customize_changeset_status' if it is being auto saved for edit box to avoid revisions. + * + * @return {void} + */ + prefilterAjax: function prefilterAjax() { + var snapshot = this, removeParam, isSameStatus; + + if ( ! api.state.has( 'changesetStatus' ) ) { + return; + } + + removeParam = function( queryString, parameter ) { + var pars = queryString.split( /[&;]/g ); + + _.each( pars, function( string, index ) { + if ( string && string.lastIndexOf( parameter, 0 ) !== -1 ) { + pars.splice( index, 1 ); + } + } ); + + return pars.join( '&' ); + }; + + $.ajaxPrefilter( function( options, originalOptions ) { + if ( ! originalOptions.data || ! snapshot.editBoxAutoSaveTriggered ) { + return; + } + + isSameStatus = api.state( 'changesetStatus' ).get() === originalOptions.data.customize_changeset_status; + if ( 'customize_save' === originalOptions.data.action && options.data && originalOptions.data.customize_changeset_status && isSameStatus ) { + options.data = removeParam( options.data, 'customize_changeset_status' ); + snapshot.editBoxAutoSaveTriggered = false; + } + } ); + }, + + /** + * Remove a param from close button link. + * + * @param {string} targetParam param. + * @return {void}. + */ + removeParamFromClose: function removeParamFromClose( targetParam ) { + var snapshot = this, closeButton, queryString, updatedParams; + closeButton = $( '.customize-controls-close' ); + queryString = closeButton.prop( 'search' ).substr( 1 ); + + if ( ! queryString.length ) { + return; + } + + updatedParams = snapshot.parseQueryString( queryString ); + delete updatedParams[ targetParam ]; + closeButton.prop( 'search', $.param( updatedParams ) ); + }, + + /** + * Parse query string. + * + * @since 4.7.0 + * @access public + * + * @param {string} queryString Query string. + * @returns {object} Parsed query string. + */ + parseQueryString: api.utils.parseQueryString + } ); +})( wp.customize, jQuery ); diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index ed72de95..0cd2fa9b 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -1,1228 +1,31 @@ -/* global jQuery, wp, JSON, _customizeSnapshotsSettings */ +/* global wp */ /* eslint no-magic-numbers: [ "error", { "ignore": [0,1,-1] } ], consistent-this: [ "error", "snapshot" ] */ -(function( api, $ ) { +(function( api ) { 'use strict'; - var escKeyCode = 27; - api.Snapshots = api.Class.extend( { + // @todo Add stuff. data: { - action: '', - uuid: '', editLink: '', title: '', - publishDate: '', - postStatus: '', - currentUserCanPublish: true, - initialServerDate: '', - initialServerTimestamp: 0, - initialClientTimestamp: 0, - previewingTheme: '', - i18n: {}, - dirty: false + i18n: {} }, - uuidParam: 'customize_changeset_uuid', - initialize: function initialize( snapshotsConfig ) { var snapshot = this; - snapshot.schedule = {}; - if ( _.isObject( snapshotsConfig ) ) { _.extend( snapshot.data, snapshotsConfig ); } - // Set the initial client timestamp. - snapshot.data.initialClientTimestamp = snapshot.dateValueOf(); - api.bind( 'ready', function() { - api.state.create( 'snapshot-exists', false ); - api.state.create( 'snapshot-saved', true ); - api.state.create( 'snapshot-submitted', true ); - - snapshot.data.uuid = snapshot.data.uuid || api.settings.changeset.uuid; - snapshot.data.title = snapshot.data.title || snapshot.data.uuid; - - snapshot.editBoxAutoSaveTriggered = false; - - if ( api.state.has( 'changesetStatus' ) && api.state( 'changesetStatus' ).get() ) { - api.state( 'snapshot-exists' ).set( true ); - } - - snapshot.extendPreviewerQuery(); - - snapshot.editControlSettings = new api.Values(); - snapshot.editControlSettings.create( 'title', snapshot.data.title ); - snapshot.editControlSettings.create( 'date', snapshot.data.publishDate ); - - api.bind( 'change', function() { - api.state( 'snapshot-submitted' ).set( false ); - } ); - - snapshot.frontendPreviewUrl = new api.Value( api.previewer.previewUrl.get() ); - snapshot.frontendPreviewUrl.link( api.previewer.previewUrl ); - snapshot.isNotSavedPreviewingTheme = false; - - snapshot.addButtons(); - snapshot.editSnapshotUI(); - snapshot.prefilterAjax(); + // @todo Add snapshot-exists, snapshot-saved, snapshot-submitted states for back-compat? Skip if they are not used. api.trigger( 'snapshots-ready', snapshot ); } ); - - api.bind( 'save', function( request ) { - - request.fail( function( response ) { - var id = '#snapshot-dialog-error', - snapshotDialogPublishError = wp.template( 'snapshot-dialog-error' ); - - if ( response.responseText ) { - - // Insert the dialog error template. - if ( 0 === $( id ).length ) { - $( 'body' ).append( snapshotDialogPublishError( { - title: snapshot.data.i18n.publish, - message: api.state( 'snapshot-exists' ).get() ? snapshot.data.i18n.permsMsg.update : snapshot.data.i18n.permsMsg.save - } ) ); - } - - snapshot.spinner.removeClass( 'is-active' ); - - $( id ).dialog( { - autoOpen: true, - modal: true - } ); - } - } ); - - return request; - } ); - }, - - /** - * Get state query vars. - * - * @return {{}} Query vars for scroll, device, url, and autofocus. - */ - getStateQueryVars: function() { - var snapshot = this, queryVars; - - queryVars = { - 'autofocus[control]': null, - 'autofocus[section]': null, - 'autofocus[panel]': null - }; - - queryVars.scroll = parseInt( api.previewer.scroll, 10 ) || 0; - queryVars.device = api.previewedDevice.get(); - queryVars.url = api.previewer.previewUrl.get(); - - if ( ! api.state( 'activated' ).get() || snapshot.isNotSavedPreviewingTheme ) { - queryVars.previewing_theme = true; - } - - _.find( [ 'control', 'section', 'panel' ], function( constructType ) { - var found = false; - api[ constructType ].each( function( construct ) { // @todo Core needs to support more Backbone methods on wp.customize.Values(). - if ( ! found && construct.expanded && construct.expanded.get() ) { - queryVars[ 'autofocus[' + constructType + ']' ] = construct.id; - found = true; - } - } ); - return found; - } ); - - return queryVars; - }, - - /** - * Update snapshot. - * - * @param {string} status post status. - * @returns {jQuery.promise} Request or promise. - */ - updateSnapshot: function updateSnapshot( status ) { - var snapshot = this, inputDate, - deferred = new $.Deferred(), - request, - requestData = { - status: status - }; - - if ( snapshot.statusButton && snapshot.statusButton.needConfirm ) { - snapshot.statusButton.disbleButton.set( false ); - snapshot.statusButton.updateButtonText( 'confirm-text' ); - snapshot.statusButton.needConfirm = false; - return deferred.promise(); - } - - if ( snapshot.snapshotTitle && snapshot.snapshotTitle.val() && 'publish' !== status ) { - requestData.title = snapshot.editControlSettings( 'title' ).get(); - } - - if ( ! _.isEmpty( snapshot.editContainer ) && snapshot.isFutureDate() && 'publish' !== status ) { - inputDate = snapshot.getDateFromInputs(); - requestData.date = snapshot.formatDate( inputDate ); - } - - if ( 'future' === status ) { - if ( requestData.date ) { - request = snapshot.sendUpdateSnapshotRequest( requestData ); - } - } else { - request = snapshot.sendUpdateSnapshotRequest( requestData ); - } - - return request ? request : deferred.promise(); - }, - - /** - * Make the AJAX request to update/save a snapshot. - * - * @param {object} options Options. - * @param {string} options.status The post status for the snapshot. - * @return {object} request. - */ - sendUpdateSnapshotRequest: function sendUpdateSnapshotRequest( options ) { - var snapshot = this, - request, data, isPublishStatus; - - data = _.extend( - { - status: 'draft' - }, - options - ); - - api.state( 'snapshot-saved' ).set( false ); - snapshot.statusButton.disable( true ); - snapshot.spinner.addClass( 'is-active' ); - - request = api.previewer.save( data ); - - isPublishStatus = 'publish' === data.status; - - request.always( function( response ) { - snapshot.spinner.removeClass( 'is-active' ); - if ( response.edit_link ) { - snapshot.data.editLink = response.edit_link; - } - if ( response.publish_date ) { - snapshot.data.publishDate = response.publish_date; - } - if ( response.title ) { - snapshot.data.title = response.title; - } - - snapshot.data.dirty = false; - } ); - - request.done( function( response ) { - var url = api.previewer.previewUrl(), - customizeUrl = window.location.href, - savedDelay = 400; - - /*** - * Delay because api.Posts.updateSettingsQuietly updates the settings after save, which triggers - * api change causing the publish button to get enabled again. - */ - _.delay( function() { - api.state( 'snapshot-saved' ).set( true ); - if ( 'pending' === data.status ) { - api.state( 'snapshot-submitted' ).set( true ); - } - }, savedDelay ); - - api.state( 'snapshot-exists' ).set( ! isPublishStatus ); - - snapshot.statusButton.disableSelect.set( isPublishStatus ); - snapshot.statusButton.disbleButton.set( true ); - snapshot.snapshotExpandButton.toggle( ! isPublishStatus ); - snapshot.previewLink.toggle( ! isPublishStatus ); - - if ( isPublishStatus ) { - snapshot.removeParamFromClose( 'customize_changeset_uuid' ); - } - - snapshot.statusButton.updateButtonText( 'alt-text' ); - - // Trigger an event for plugins to use. - api.trigger( 'customize-snapshots-update', { - previewUrl: url, - customizeUrl: customizeUrl, - uuid: snapshot.data.uuid, - response: response - } ); - } ); - - request.fail( function( response ) { - var id = '#snapshot-dialog-error', - snapshotDialogShareError = wp.template( 'snapshot-dialog-error' ), - messages = snapshot.data.i18n.errorMsg, - invalidityCount = 0, - dialogElement; - - snapshot.statusButton.disableSelect.set( false ); - snapshot.statusButton.disbleButton.set( false ); - - if ( response.setting_validities ) { - invalidityCount = _.size( response.setting_validities, function( validity ) { - return true !== validity; - } ); - } - - /* - * Short-circuit if there are setting validation errors, since the error messages - * will be displayed with the controls themselves. Eventually, once we have - * a global notification area in the Customizer, we can eliminate this - * short-circuit and instead display the messages in there. - * See https://core.trac.wordpress.org/ticket/35210 - */ - if ( invalidityCount > 0 ) { - return; - } - - if ( response.errors ) { - messages += ' ' + _.pluck( response.errors, 'message' ).join( ' ' ); - } - - // Insert the snapshot dialog error template. - dialogElement = $( id ); - if ( ! dialogElement.length ) { - dialogElement = $( snapshotDialogShareError( { - title: snapshot.data.i18n.errorTitle, - message: messages - } ) ); - $( 'body' ).append( dialogElement ); - } - - // Open the dialog. - $( id ).dialog( { - autoOpen: true, - modal: true - } ); - } ); - - return request; - }, - - /** - * Create the snapshot buttons. - * - * @return {void} - */ - addButtons: function addButtons() { - var snapshot = this, disableButton = true, disableSelectButton = true, - setPreviewLinkHref, currentTheme, savedPreviewingTheme, themeNotActiveOrSaved; - - snapshot.spinner = $( '#customize-header-actions' ).find( '.spinner' ); - snapshot.publishButton = $( '#save' ); - - snapshot.publishButton.addClass( 'hidden' ); - snapshot.statusButton = snapshot.addStatusButton(); - - if ( api.state( 'changesetStatus' ).get() ) { - disableSelectButton = false; - if ( 'auto-draft' === api.state( 'changesetStatus' ).get() ) { - disableButton = false; - } else { - snapshot.statusButton.updateButtonText( 'alt-text' ); - } - } - - currentTheme = api.settings.theme.stylesheet; // Or previewing theme. - savedPreviewingTheme = snapshot.data.previewingTheme; - themeNotActiveOrSaved = ! api.state( 'activated' ).get() && ! savedPreviewingTheme; - snapshot.isNotSavedPreviewingTheme = savedPreviewingTheme && savedPreviewingTheme !== currentTheme; - - if ( themeNotActiveOrSaved || snapshot.isNotSavedPreviewingTheme ) { - disableButton = false; - disableSelectButton = false; - } - - snapshot.statusButton.disbleButton.set( disableButton ); - snapshot.statusButton.disableSelect.set( disableSelectButton ); - - // Preview link. - snapshot.previewLink = $( $.trim( wp.template( 'snapshot-preview-link' )() ) ); - snapshot.previewLink.toggle( api.state( 'snapshot-saved' ).get() ); - snapshot.previewLink.attr( 'target', snapshot.data.uuid ); - setPreviewLinkHref = _.debounce( function() { - var queryVars; - if ( api.state( 'snapshot-exists' ).get() ) { - snapshot.previewLink.attr( 'href', snapshot.getSnapshotFrontendPreviewUrl() ); - } else { - snapshot.previewLink.attr( 'href', snapshot.frontendPreviewUrl.get() ); - } - - // Add the customize_theme param to the frontend URL if the theme is not active. - if ( ! api.state( 'activated' ).get() ) { - queryVars = snapshot.parseQueryString( snapshot.previewLink.prop( 'search' ).substr( 1 ) ); - queryVars.customize_theme = api.settings.theme.stylesheet; - snapshot.previewLink.prop( 'search', $.param( queryVars ) ); - } - } ); - snapshot.frontendPreviewUrl.bind( setPreviewLinkHref ); - setPreviewLinkHref(); - api.state.bind( 'change', setPreviewLinkHref ); - api.bind( 'saved', setPreviewLinkHref ); - snapshot.statusButton.container.after( snapshot.previewLink ); - api.state( 'snapshot-saved' ).bind( function( saved ) { - snapshot.previewLink.toggle( saved ); - } ); - - // Edit button. - snapshot.snapshotExpandButton = $( $.trim( wp.template( 'snapshot-expand-button' )( {} ) ) ); - snapshot.statusButton.container.after( snapshot.snapshotExpandButton ); - - if ( ! snapshot.data.editLink ) { - snapshot.snapshotExpandButton.hide(); - snapshot.previewLink.hide(); - } - - api.state( 'change', function() { - snapshot.snapshotExpandButton.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() ); - } ); - - api.state( 'snapshot-exists' ).bind( function( exist ) { - snapshot.snapshotExpandButton.toggle( exist ); - snapshot.previewLink.toggle( exist ); - } ); - - api.bind( 'change', function() { - if ( api.state( 'snapshot-saved' ).get() ) { - snapshot.statusButton.disable( false ); - if ( snapshot.statusButton.button.data( 'confirm-text' ) !== snapshot.statusButton.buttonText.get() ) { - snapshot.statusButton.updateButtonText( 'button-text' ); - } - if ( snapshot.submitButton ) { - snapshot.submitButton.prop( 'disabled', false ); - } - if ( snapshot.saveButton ) { - snapshot.saveButton.prop( 'disabled', false ); - } - api.state( 'snapshot-saved' ).set( false ); - } - } ); - - if ( ! snapshot.data.currentUserCanPublish ) { - snapshot.addSubmitButton(); - snapshot.addSaveButton(); - } - }, - - /** - * Adds Submit Button when user does not have 'customize_publish' permission. - * - * @return {void} - */ - addSubmitButton: function() { - var snapshot = this, disableSubmitButton; - - disableSubmitButton = 'pending' === snapshot.data.postStatus || ! api.state( 'snapshot-exists' ).get(); - - if ( snapshot.statusButton ) { - snapshot.statusButton.container.hide(); - } else { - snapshot.publishButton.hide(); - } - - snapshot.submitButton = $( $.trim( wp.template( 'snapshot-submit' )( { - buttonText: snapshot.data.i18n.submit - } ) ) ); - - snapshot.submitButton.prop( 'disabled', disableSubmitButton ); - snapshot.submitButton.insertBefore( snapshot.publishButton ); - api.state( 'snapshot-submitted' ).bind( function( submitted ) { - snapshot.submitButton.prop( 'disabled', submitted ); - } ); - - snapshot.submitButton.on( 'click', function( event ) { - event.preventDefault(); - snapshot.submitButton.prop( 'disabled', true ); - if ( snapshot.saveButton ) { - snapshot.saveButton.prop( 'disabled', true ); - } - snapshot.updateSnapshot( 'pending' ).fail( function() { - snapshot.submitButton.prop( 'disabled', false ); - } ); - } ); - - snapshot.editControlSettings.bind( 'change', function() { - if ( api.state( 'snapshot-saved' ).get() ) { - snapshot.submitButton.prop( 'disabled', false ); - } - } ); - }, - - /** - * Adds Save Button when user does not have 'customize_publish' permission. - * - * @return {void} - */ - addSaveButton: function() { - var snapshot = this, disableSaveButton, isSaved; - - isSaved = _.contains( [ 'future', 'pending', 'draft' ], api.state( 'changesetStatus' ).get() ); - disableSaveButton = isSaved || ! api.state( 'snapshot-exists' ).get(); - - snapshot.saveButton = $( $.trim( wp.template( 'snapshot-save' )( { - buttonText: isSaved ? snapshot.data.i18n.updateButton : snapshot.data.i18n.saveButton - } ) ) ); - - snapshot.saveButton.prop( 'disabled', disableSaveButton ); - snapshot.saveButton.insertBefore( snapshot.publishButton ); - - api.state( 'snapshot-submitted' ).bind( function( submitted ) { - if ( submitted ) { - snapshot.saveButton.prop( 'disabled', true ); - } - } ); - - snapshot.saveButton.on( 'click', function( event ) { - event.preventDefault(); - snapshot.saveButton.prop( 'disabled', true ); - snapshot.submitButton.prop( 'disabled', true ); - snapshot.updateSnapshot( 'draft' ).done( function() { - snapshot.saveButton.prop( 'disabled', true ); - snapshot.submitButton.prop( 'disabled', false ); - snapshot.saveButton.text( snapshot.data.i18n.updateButton ); - } ).fail( function() { - snapshot.saveButton.prop( 'disabled', false ); - snapshot.submitButton.prop( 'disabled', false ); - } ); - } ); - - snapshot.editControlSettings.bind( 'change', function() { - if ( api.state( 'snapshot-saved' ).get() ) { - snapshot.saveButton.prop( 'disabled', false ); - } - } ); - }, - - /** - * Renders snapshot schedule and handles it's events. - * - * @returns {void} - */ - editSnapshotUI: function editSnapshotUI() { - var snapshot = this, sliceBegin = 0, - sliceEnd = -2, updateUI, toggleDateNotification; - - snapshot.snapshotEditContainerDisplayed = new api.Value( false ); - - updateUI = function() { - snapshot.populateSetting(); - }; - - // Inject the UI. - if ( _.isEmpty( snapshot.editContainer ) ) { - if ( '0000-00-00 00:00:00' === snapshot.data.publishDate ) { - snapshot.data.publishDate = snapshot.getCurrentTime(); - } - - // Normalize date with secs set as zeros removed. - snapshot.data.publishDate = snapshot.data.publishDate.slice( sliceBegin, sliceEnd ) + '00'; - - // Extend the snapshots data object and add the parsed datetime strings. - snapshot.data = _.extend( snapshot.data, snapshot.parseDateTime( snapshot.data.publishDate ) ); - - // Add the template to the DOM. - snapshot.editContainer = $( $.trim( wp.template( 'snapshot-edit-container' )( snapshot.data ) ) ); - snapshot.editContainer.hide().appendTo( $( '#customize-header-actions' ) ); - snapshot.dateNotification = snapshot.editContainer.find( '.snapshot-future-date-notification' ); - snapshot.countdown = snapshot.editContainer.find( '.snapshot-scheduled-countdown' ); - snapshot.dateControl = snapshot.editContainer.find( '.snapshot-control-date' ); - - if ( snapshot.data.currentUserCanPublish ) { - - // Store the date inputs. - snapshot.schedule.inputs = snapshot.editContainer.find( '.date-input' ); - - snapshot.schedule.inputs.on( 'input', updateUI ); - - snapshot.schedule.inputs.on( 'blur', function() { - snapshot.populateInputs(); - updateUI(); - } ); - - snapshot.updateCountdown(); - - snapshot.editContainer.find( '.reset-time a' ).on( 'click', function( event ) { - event.preventDefault(); - snapshot.updateSnapshotEditControls(); - } ); - } - - if ( snapshot.statusButton && 'future' !== snapshot.statusButton.value.get() ) { - snapshot.countdown.hide(); - } - - snapshot.snapshotTitle = snapshot.editContainer.find( '#snapshot-title' ); - snapshot.snapshotTitle.on( 'input', updateUI ); - } - - toggleDateNotification = function() { - if ( ! _.isEmpty( snapshot.dateNotification ) ) { - snapshot.dateNotification.toggle( ! snapshot.isFutureDate() ); - } - }; - - // Set up toggling of the schedule container. - snapshot.snapshotEditContainerDisplayed.bind( function( isDisplayed ) { - - if ( snapshot.statusButton ) { - snapshot.dateControl.toggle( 'future' === snapshot.statusButton.value.get() ); - } - - if ( isDisplayed ) { - snapshot.editContainer.stop().slideDown( 'fast' ).attr( 'aria-expanded', 'true' ); - snapshot.snapshotExpandButton.attr( 'aria-pressed', 'true' ); - snapshot.snapshotExpandButton.prop( 'title', snapshot.data.i18n.collapseSnapshotScheduling ); - toggleDateNotification(); - } else { - snapshot.editContainer.stop().slideUp( 'fast' ).attr( 'aria-expanded', 'false' ); - snapshot.snapshotExpandButton.attr( 'aria-pressed', 'false' ); - snapshot.snapshotExpandButton.prop( 'title', snapshot.data.i18n.expandSnapshotScheduling ); - } - } ); - - snapshot.editControlSettings( 'date' ).bind( function() { - toggleDateNotification(); - } ); - - // Toggle schedule container when clicking the button. - snapshot.snapshotExpandButton.on( 'click', function() { - snapshot.snapshotEditContainerDisplayed.set( ! snapshot.snapshotEditContainerDisplayed.get() ); - } ); - - // Collapse the schedule container when Esc is pressed while the button is focused. - snapshot.snapshotExpandButton.on( 'keydown', function( event ) { - if ( escKeyCode === event.which && snapshot.snapshotEditContainerDisplayed.get() ) { - event.stopPropagation(); - event.preventDefault(); - snapshot.snapshotEditContainerDisplayed.set( false ); - } - } ); - - // Collapse the schedule container when Esc is pressed inside of the schedule container. - snapshot.editContainer.on( 'keydown', function( event ) { - if ( escKeyCode === event.which && snapshot.snapshotEditContainerDisplayed.get() ) { - event.stopPropagation(); - event.preventDefault(); - snapshot.snapshotEditContainerDisplayed.set( false ); - snapshot.snapshotExpandButton.focus(); - } - } ); - - // Collapse the schedule container interacting outside the schedule container. - $( 'body' ).on( 'mousedown', function( event ) { - var isDisplayed = snapshot.snapshotEditContainerDisplayed.get(), - isTargetEditContainer = snapshot.editContainer.is( event.target ) || 0 !== snapshot.editContainer.has( event.target ).length, - isTargetExpandButton = snapshot.snapshotExpandButton.is( event.target ); - - if ( isDisplayed && ! isTargetEditContainer && ! isTargetExpandButton ) { - snapshot.snapshotEditContainerDisplayed.set( false ); - } - } ); - - snapshot.snapshotEditContainerDisplayed.set( false ); - - api.state( 'snapshot-saved' ).bind( function( saved ) { - if ( saved && ! snapshot.dirtyEditControlValues ) { - snapshot.updateSnapshotEditControls(); - } - } ); - - api.bind( 'change', function() { - snapshot.data.dirty = true; - snapshot.editContainer.find( 'a.snapshot-edit-link' ).hide(); - } ); - - api.state( 'snapshot-exists' ).bind( function( exists ) { - if ( exists && ! _.isEmpty( snapshot.editContainer ) ) { - snapshot.updateSnapshotEditControls(); - } else { - snapshot.snapshotEditContainerDisplayed.set( false ); - } - } ); - - if ( snapshot.statusButton ) { - snapshot.updateSnapshotEditControls(); - } - - snapshot.autoSaveEditBox(); - }, - - /** - * Auto save the edit box values. - * - * @return {void} - */ - autoSaveEditBox: function() { - var snapshot = this, update, - delay = 2000, status, isFutureDateAndStatus; - - snapshot.updatePending = false; - snapshot.dirtyEditControlValues = false; - - update = _.debounce( function() { - status = snapshot.statusButton.value.get(); - isFutureDateAndStatus = 'future' === status && ! snapshot.isFutureDate(); - if ( 'publish' === status || isFutureDateAndStatus ) { - snapshot.updatePending = false; - return; - } - snapshot.updatePending = true; - snapshot.editBoxAutoSaveTriggered = true; - snapshot.dirtyEditControlValues = false; - snapshot.updateSnapshot( status ).done( function() { - snapshot.updatePending = snapshot.dirtyEditControlValues; - if ( ! snapshot.updatePending ) { - snapshot.updateSnapshotEditControls(); - } else if ( snapshot.dirtyEditControlValues ) { - update(); - } - snapshot.dirtyEditControlValues = false; - } ).fail( function() { - snapshot.updatePending = false; - snapshot.dirtyEditControlValues = true; - } ); - }, delay ); - - snapshot.editControlSettings( 'title' ).bind( function() { - snapshot.dirtyEditControlValues = true; - if ( ! snapshot.updatePending ) { - update(); - } - } ); - - snapshot.editControlSettings( 'date' ).bind( function() { - if ( snapshot.isFutureDate() ) { - snapshot.dirtyEditControlValues = true; - if ( ! snapshot.updatePending ) { - update(); - } - } - } ); - - $( window ).on( 'beforeunload.customize-confirm', function() { - if ( snapshot.updatePending || snapshot.dirtyEditControlValues ) { - return snapshot.data.i18n.aysMsg; - } - return undefined; - } ); - - // @todo Show loader and disable button while auto saving. - - api.bind( 'changeset-saved', function() { - if ( 'auto-draft' !== api.state( 'changesetStatus' ).get() ) { - api.state( 'saved' ).set( true ); // Suppress the AYS dialog. - } - }); - }, - - /** - * Get the preview URL with the snapshot UUID attached. - * - * @returns {string} URL. - */ - getSnapshotFrontendPreviewUrl: function getSnapshotFrontendPreviewUrl() { - var snapshot = this, a = document.createElement( 'a' ); - a.href = snapshot.frontendPreviewUrl.get(); - if ( a.search ) { - a.search += '&'; - } - a.search += snapshot.uuidParam + '=' + snapshot.data.uuid; - return a.href; - }, - - /** - * Updates snapshot schedule with `snapshot.data`. - * - * @return {void} - */ - updateSnapshotEditControls: function updateSnapshotEditControls() { - var snapshot = this, - parsed, - status, - sliceBegin = 0, - sliceEnd = -2; - - if ( _.isEmpty( snapshot.editContainer ) ) { - return; - } - - status = api.state( 'changesetStatus' ).get(); - - if ( snapshot.data.currentUserCanPublish ) { - if ( '0000-00-00 00:00:00' === snapshot.data.publishDate || ! status || 'auto-draft' === status ) { - snapshot.data.publishDate = snapshot.getCurrentTime(); - } - - // Normalize date with seconds removed. - snapshot.data.publishDate = snapshot.data.publishDate.slice( sliceBegin, sliceEnd ) + '00'; - parsed = snapshot.parseDateTime( snapshot.data.publishDate ); - - // Update date controls. - snapshot.schedule.inputs.each( function() { - var input = $( this ), - fieldName = input.data( 'date-input' ); - $( this ).val( parsed[ fieldName ] ); - } ); - } - - snapshot.editContainer.find( 'a.snapshot-edit-link' ) - .attr( 'href', snapshot.data.editLink ) - .show(); - if ( ! _.isEmpty( snapshot.data.title ) ) { - snapshot.snapshotTitle.val( snapshot.data.title ); - } - snapshot.populateSetting(); - }, - - /** - * Update the scheduled countdown text. - * - * Hides countdown if post_status is not already future. - * Toggles the countdown if there is no remaining time. - * - * @returns {boolean} True if date inputs are valid. - */ - updateCountdown: function updateCountdown() { - var snapshot = this, - countdownTemplate = wp.template( 'snapshot-scheduled-countdown' ), - dateTimeFromInput = snapshot.getDateFromInputs(), - millisecondsDivider = 1000, - remainingTime; - - if ( ! dateTimeFromInput ) { - return false; - } - - remainingTime = dateTimeFromInput.valueOf(); - remainingTime -= snapshot.dateValueOf( snapshot.getCurrentTime() ); - remainingTime = Math.ceil( remainingTime / millisecondsDivider ); - - if ( 0 < remainingTime ) { - snapshot.countdown.text( countdownTemplate( { - remainingTime: remainingTime - } ) ); - snapshot.countdown.show(); - } else { - snapshot.countdown.hide(); - } - - return true; - }, - - /** - * Get date from inputs. - * - * @returns {Date|null} Date created from inputs or null if invalid date. - */ - getDateFromInputs: function getDateFromInputs() { - var snapshot = this, - template = snapshot.editContainer, - monthOffset = 1, - date; - - date = new Date( - parseInt( template.find( '[data-date-input="year"]' ).val(), 10 ), - parseInt( template.find( '[data-date-input="month"]' ).val(), 10 ) - monthOffset, - parseInt( template.find( '[data-date-input="day"]' ).val(), 10 ), - parseInt( template.find( '[data-date-input="hour"]' ).val(), 10 ), - parseInt( template.find( '[data-date-input="minute"]' ).val(), 10 ) - ); - - if ( isNaN( date.valueOf() ) ) { - return null; - } - - date.setSeconds( 0 ); - - return date; - }, - - /** - * Parse datetime string. - * - * @param {string} datetime Date/Time string. - * @returns {object|null} Returns object containing date components or null if parse error. - */ - parseDateTime: function parseDateTime( datetime ) { - var matches = datetime.match( /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ ); - - if ( ! matches ) { - return null; - } - - matches.shift(); - - return { - year: matches.shift(), - month: matches.shift(), - day: matches.shift(), - hour: matches.shift(), - minute: matches.shift(), - second: matches.shift() - }; - }, - - /** - * Format a Date Object. Returns 'Y-m-d H:i:s' format. - * - * @props http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript#comment33639551_10073699 - * - * @param {Date} date A Date object. - * @returns {string} A formatted date String. - */ - formatDate: function formatDate( date ) { - var formattedDate, - yearLength = 4, - nonYearLength = 2, - monthOffset = 1; - - formattedDate = ( '0000' + String( date.getFullYear() ) ).substr( -yearLength, yearLength ); - formattedDate += '-' + ( '00' + String( date.getMonth() + monthOffset ) ).substr( -nonYearLength, nonYearLength ); - formattedDate += '-' + ( '00' + String( date.getDate() ) ).substr( -nonYearLength, nonYearLength ); - formattedDate += ' ' + ( '00' + String( date.getHours() ) ).substr( -nonYearLength, nonYearLength ); - formattedDate += ':' + ( '00' + String( date.getMinutes() ) ).substr( -nonYearLength, nonYearLength ); - formattedDate += ':' + ( '00' + String( date.getSeconds() ) ).substr( -nonYearLength, nonYearLength ); - - return formattedDate; - }, - - /** - * Populate inputs from the setting value, if none of them are currently focused. - * - * @returns {boolean} Whether the inputs were populated. - */ - populateInputs: function populateInputs() { - var snapshot = this, parsed; - - if ( snapshot.schedule.inputs.is( ':focus' ) || '0000-00-00 00:00:00' === snapshot.data.publishDate ) { - return false; - } - - parsed = snapshot.parseDateTime( snapshot.data.publishDate ); - if ( ! parsed ) { - return false; - } - - snapshot.schedule.inputs.each( function() { - var input = $( this ), - fieldName = input.data( 'date-input' ); - - if ( ! $( this ).is( 'select' ) && '' === $( this ).val() ) { - $( this ).val( parsed[fieldName] ); - } - } ); - return true; - }, - - /** - * Populate setting value from the inputs. - * - * @returns {void} - */ - populateSetting: function populateSetting() { - var snapshot = this, - date = snapshot.getDateFromInputs(), - scheduled; - - snapshot.editControlSettings( 'title' ).set( snapshot.snapshotTitle.val() ); - - if ( ! date || ! snapshot.data.currentUserCanPublish ) { - return; - } - - date.setSeconds( 0 ); - scheduled = snapshot.formatDate( date ) !== snapshot.data.publishDate; - snapshot.editControlSettings( 'date' ).set( snapshot.formatDate( date ) ); - - if ( 'future' === snapshot.statusButton.value.get() ) { - snapshot.updateCountdown(); - } - - snapshot.editContainer.find( '.reset-time' ).toggle( scheduled ); - }, - - /** - * Check if the schedule date is in the future. - * - * @returns {boolean} True if future date. - */ - isFutureDate: function isFutureDate() { - var snapshot = this, - date = snapshot.getDateFromInputs(), - millisecondsDivider = 1000, - remainingTime; - - if ( ! date ) { - return false; - } - - remainingTime = snapshot.dateValueOf( date ); - remainingTime -= snapshot.dateValueOf( snapshot.getCurrentTime() ); - remainingTime = Math.ceil( remainingTime / millisecondsDivider ); - return 0 < remainingTime; - }, - - /** - * Get current date/time in the site's timezone. - * - * Same functionality as the `current_time( 'mysql', false )` function in PHP. - * - * @returns {string} Current datetime string. - */ - getCurrentTime: function getCurrentTime() { - var snapshot = this, currentDate, currentTimestamp, timestampDifferential; - currentTimestamp = ( new Date() ).valueOf(); - currentDate = snapshot.parsePostDate( snapshot.data.initialServerDate ); - timestampDifferential = currentTimestamp - snapshot.data.initialClientTimestamp; - timestampDifferential += snapshot.data.initialClientTimestamp - snapshot.data.initialServerTimestamp; - currentDate.setTime( currentDate.getTime() + timestampDifferential ); - return snapshot.formatDate( currentDate ); - }, - - /** - * Parse post date string in YYYY-MM-DD HH:MM:SS format (local timezone). - * - * @param {string} postDate Post date string. - * @returns {Date} Parsed date. - */ - parsePostDate: function parsePostDate( postDate ) { - var dateParts = _.map( postDate.split( /\D/ ), function( datePart ) { - return parseInt( datePart, 10 ); - } ); - return new Date( dateParts[0], dateParts[1] - 1, dateParts[2], dateParts[3], dateParts[4], dateParts[5] ); // eslint-disable-line no-magic-numbers - }, - - /** - * Get the primitive value of a Date object. - * - * @param {string|Date} [dateString] The post status for the snapshot. - * @returns {object|string} The primitive value or date object. - */ - dateValueOf: function dateValueOf( dateString ) { - var date; - - if ( 'string' === typeof dateString ) { - date = new Date( dateString ); - } else if ( dateString instanceof Date ) { - date = dateString; - } else { - date = new Date(); - } - - return date.valueOf(); - }, - - /** - * Amend the preview query so we can update the snapshot during `changeset_save`. - * - * @return {void} - */ - extendPreviewerQuery: function extendPreviewerQuery() { - var snapshot = this, originalQuery = api.previewer.query; - - api.previewer.query = function() { - var retval = originalQuery.apply( this, arguments ); - - if ( ! _.contains( [ 'future', 'pending', 'draft' ], snapshot.statusButton.value.get() ) ) { - return retval; - } - - retval.customizer_state_query_vars = JSON.stringify( snapshot.getStateQueryVars() ); - - if ( snapshot.editControlSettings( 'title' ).get() ) { - retval.customize_changeset_title = snapshot.editControlSettings( 'title' ).get(); - } - if ( snapshot.editControlSettings( 'date' ).get() && snapshot.isFutureDate() ) { - retval.customize_changeset_date = snapshot.editControlSettings( 'date' ).get(); - } - return retval; - }; - }, - - /** - * Add status button. - * - * @return {object} status button. - */ - addStatusButton: function addStatusButton() { - var snapshot = this, selectMenuButton, statusButton, selectedOption, buttonText, changesetStatus, selectedStatus; - changesetStatus = api.state( 'changesetStatus' ).get(); - statusButton = {}; - - selectedStatus = changesetStatus && 'auto-draft' !== changesetStatus ? changesetStatus : 'publish'; - - statusButton.value = new api.Value( selectedStatus ); - statusButton.disbleButton = new api.Value(); - statusButton.disableSelect = new api.Value(); - statusButton.buttonText = new api.Value(); - statusButton.needConfirm = false; - - statusButton.container = $( $.trim( wp.template( 'snapshot-status-button' )({ - selected: selectedStatus - }) ) ); - statusButton.button = statusButton.container.find( '.snapshot-status-button-overlay' ); - statusButton.select = statusButton.container.find( 'select' ); - statusButton.select.selectmenu({ - width: 'auto', - icons: { - button: 'dashicons dashicons-arrow-down' - }, - change: function( event, ui ) { - statusButton.value.set( ui.item.value ); - }, - select: function() { - if ( statusButton.hiddenButton ) { - statusButton.hiddenButton.text( statusButton.buttonText.get() ); - } - } - }); - - selectMenuButton = statusButton.container.find( '.ui-selectmenu-button' ); - statusButton.hiddenButton = selectMenuButton.find( '.ui-selectmenu-text' ); - statusButton.hiddenButton.addClass( 'button button-primary' ); - - statusButton.dropDown = selectMenuButton.find( '.ui-icon' ); - statusButton.dropDown.addClass( 'button button-primary' ); - - statusButton.updateButtonText = function( dataAttr ) { - buttonText = statusButton.button.data( dataAttr ); - statusButton.button.text( buttonText ); - statusButton.hiddenButton.text( buttonText ); - statusButton.buttonText.set( buttonText ); - }; - - statusButton.value.bind( function( status ) { - selectedOption = statusButton.select.find( 'option:selected' ); - statusButton.button.data( 'alt-text', selectedOption.data( 'alt-text' ) ); - statusButton.button.data( 'button-text', selectedOption.text() ); - statusButton.updateButtonText( 'button-text' ); - - if ( 'publish' === status ) { - snapshot.snapshotExpandButton.hide(); - statusButton.button.data( 'confirm-text', selectedOption.data( 'confirm-text' ) ); - statusButton.button.data( 'publish-text', selectedOption.data( 'publish-text' ) ); - statusButton.needConfirm = true; - } - - if ( 'future' === status ) { - snapshot.snapshotEditContainerDisplayed.set( true ); - snapshot.snapshotExpandButton.show(); - if ( snapshot.isFutureDate() ) { - snapshot.countdown.show(); - snapshot.updateSnapshot( status ); - } - } else { - snapshot.updateSnapshot( status ); - snapshot.snapshotEditContainerDisplayed.set( false ); - snapshot.countdown.hide(); - } - } ); - - statusButton.disbleButton.bind( function( disabled ) { - statusButton.button.prop( 'disabled', disabled ); - } ); - - statusButton.disableSelect.bind( function( disabled ) { - statusButton.select.selectmenu( disabled ? 'disable' : 'enable' ); - statusButton.dropDown.toggleClass( 'disabled', disabled ); - } ); - - statusButton.disable = function( disable ) { - statusButton.disableSelect.set( disable ); - statusButton.disbleButton.set( disable ); - }; - - statusButton.button.on( 'click', function( event ) { - event.preventDefault(); - snapshot.updateSnapshot( statusButton.value.get() ); - } ); - - snapshot.publishButton.after( statusButton.container ); - - return statusButton; - }, - - /** - * Remove 'customize_changeset_status' if it is being auto saved for edit box to avoid revisions. - * - * @return {void} - */ - prefilterAjax: function prefilterAjax() { - var snapshot = this, removeParam, isSameStatus; - - if ( ! api.state.has( 'changesetStatus' ) ) { - return; - } - - removeParam = function( queryString, parameter ) { - var pars = queryString.split( /[&;]/g ); - - _.each( pars, function( string, index ) { - if ( string && string.lastIndexOf( parameter, 0 ) !== -1 ) { - pars.splice( index, 1 ); - } - } ); - - return pars.join( '&' ); - }; - - $.ajaxPrefilter( function( options, originalOptions ) { - if ( ! originalOptions.data || ! snapshot.editBoxAutoSaveTriggered ) { - return; - } - - isSameStatus = api.state( 'changesetStatus' ).get() === originalOptions.data.customize_changeset_status; - if ( 'customize_save' === originalOptions.data.action && options.data && originalOptions.data.customize_changeset_status && isSameStatus ) { - options.data = removeParam( options.data, 'customize_changeset_status' ); - snapshot.editBoxAutoSaveTriggered = false; - } - } ); - }, - - /** - * Remove a param from close button link. - * - * @param {string} targetParam param. - * @return {void}. - */ - removeParamFromClose: function removeParamFromClose( targetParam ) { - var snapshot = this, closeButton, queryString, updatedParams; - closeButton = $( '.customize-controls-close' ); - queryString = closeButton.prop( 'search' ).substr( 1 ); - - if ( ! queryString.length ) { - return; - } - - updatedParams = snapshot.parseQueryString( queryString ); - delete updatedParams[ targetParam ]; - closeButton.prop( 'search', $.param( updatedParams ) ); - }, - - /** - * Parse query string. - * - * @since 4.7.0 - * @access public - * - * @param {string} queryString Query string. - * @returns {object} Parsed query string. - */ - parseQueryString: api.utils.parseQueryString + } } ); - if ( 'undefined' !== typeof _customizeSnapshotsSettings ) { - api.snapshots = new api.Snapshots( _customizeSnapshotsSettings ); - } - -})( wp.customize, jQuery ); +})( wp.customize ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 04514851..d6b43c20 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -244,6 +244,7 @@ public function enqueue_controls_scripts() { } } + // @todo Much of this is irrelevant as of 4.9. // Script data array. $exports = apply_filters( 'customize_snapshots_export_data', array( 'editLink' => isset( $edit_link ) ? $edit_link : '', @@ -273,8 +274,8 @@ public function enqueue_controls_scripts() { wp_scripts()->add_inline_script( 'customize-snapshots', - sprintf( 'var _customizeSnapshotsSettings = %s;', wp_json_encode( $exports ) ), - 'before' + sprintf( 'wp.customize.snapshots = new wp.customize.Snapshots( %s );', wp_json_encode( $exports ) ), + 'after' ); } diff --git a/php/class-plugin.php b/php/class-plugin.php index 583c9712..52ac974b 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -68,7 +68,11 @@ public function register_scripts( \WP_Scripts $wp_scripts ) { $min = ( SCRIPT_DEBUG || $is_git_repo ? '' : '.min' ); $handle = 'customize-snapshots'; - $src = $this->dir_url . 'js/customize-snapshots' . $min . '.js'; + if ( version_compare( strtok( get_bloginfo( 'version' ), '-' ), '4.9', '>=' ) ) { + $src = $this->dir_url . 'js/customize-snapshots' . $min . '.js'; + } else { + $src = $this->dir_url . 'js/customize-snapshots-compat' . $min . '.js'; + } $deps = array( 'jquery', 'jquery-ui-dialog', 'jquery-ui-selectmenu', 'wp-util', 'customize-controls' ); $wp_scripts->add( $handle, $src, $deps ); @@ -100,7 +104,11 @@ public function register_styles( \WP_Styles $wp_styles ) { $min = ( SCRIPT_DEBUG || $is_git_repo ? '' : '.min' ); $handle = 'customize-snapshots'; - $src = $this->dir_url . 'css/customize-snapshots' . $min . '.css'; + if ( version_compare( strtok( get_bloginfo( 'version' ), '-' ), '4.9', '>=' ) ) { + $src = $this->dir_url . 'css/customize-snapshots' . $min . '.css'; + } else { + $src = $this->dir_url . 'css/customize-snapshots-compat' . $min . '.css'; + } $deps = array( 'wp-jquery-ui-dialog' ); $wp_styles->add( $handle, $src, $deps ); From 3993bffb01bae9c2ac202270ac67fe3227448290 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 30 Oct 2017 10:35:38 -0700 Subject: [PATCH 39/90] Bump versions and update readme --- composer.json | 2 +- customize-snapshots.php | 2 +- readme.md | 12 +++++++++++- readme.txt | 11 +++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 89ddf97c..1f134354 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.6.2", + "version": "0.7.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 242d8dec..a75fc12b 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.6.2 + * Version: 0.7-beta * Author: XWP * Author URI: https://xwp.co/ * License: GPLv2+ diff --git a/readme.md b/readme.md index 55ccb2cf..acc70287 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ Provide a UI for managing Customizer changesets; save changesets as named drafts **Contributors:** [xwp](https://profiles.wordpress.org/xwp), [westonruter](https://profiles.wordpress.org/westonruter), [valendesigns](https://profiles.wordpress.org/valendesigns), [utkarshpatel](https://profiles.wordpress.org/utkarshpatel), [sayedwp](https://profiles.wordpress.org/sayedwp), [newscorpau](https://profiles.wordpress.org/newscorpau) **Tags:** [customizer](https://wordpress.org/plugins/tags/customizer), [customize](https://wordpress.org/plugins/tags/customize), [changesets](https://wordpress.org/plugins/tags/changesets) -**Requires at least:** 4.6 +**Requires at least:** 4.7 **Tested up to:** 4.8.1 **Stable tag:** 0.6.2 **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) @@ -80,6 +80,16 @@ Requires PHP 5.3+. **Development of this plugin is done [on GitHub](https://gith ## Changelog ## +### 0.7.0 - ??? ### +* Added: Add compatibility with 4.9, re-using features of the plugin that have been merged into core in this release. Increase minimum required version of WordPress to 4.7. See [#162](https://github.com/xwp/wp-customize-snapshots/pull/162). +* Added: Remove hiding of Add New links for changesets in favor of just redirecting post-new.php to Customizer. See [#156](https://github.com/xwp/wp-customize-snapshots/pull/156). +* Added: Allow publishing from preview via link in admin bar. See [#115](https://github.com/xwp/wp-customize-snapshots/pull/115), [#103](https://github.com/xwp/wp-customize-snapshots/issues/103). +* Updated: Change link text for post list table action from “Edit” to “Inspect”. See [#155](https://github.com/xwp/wp-customize-snapshots/pull/155), [#153](https://github.com/xwp/wp-customize-snapshots/issues/153). +* Fixed: Prevent changeset session remembrance when browsing in preview iframe. See [#154](https://github.com/xwp/wp-customize-snapshots/pull/154). +* Added: Include required PHP version (5.3) in readme. See [#160](https://github.com/xwp/wp-customize-snapshots/pull/160), [#159](https://github.com/xwp/wp-customize-snapshots/issues/159). + +See [issues and PRs in milestone](https://github.com/xwp/wp-customize-snapshots/milestone/11?closed=1). + ### 0.6.2 - 2017-07-26 ### * Added: Just like the admin menu has Changesets link under Customize, add Changesets link in admin bar submenu item under Customize. See #143. * Fixed: Restore frontend changeset preview session resuming, to restore the previously previewed changeset in case of accidentally navigating away from frontend changeset preview. See #145. diff --git a/readme.txt b/readme.txt index 66a06a4f..c1a57d95 100644 --- a/readme.txt +++ b/readme.txt @@ -41,6 +41,17 @@ Requires PHP 5.3+. **Development of this plugin is done [on GitHub](https://gith == Changelog == += 0.7.0 - ??? = + +* Added: Add compatibility with 4.9, re-using features of the plugin that have been merged into core in this release. Increase minimum required version of WordPress to 4.7. See [#162](https://github.com/xwp/wp-customize-snapshots/pull/162). +* Added: Remove hiding of Add New links for changesets in favor of just redirecting post-new.php to Customizer. See [#156](https://github.com/xwp/wp-customize-snapshots/pull/156). +* Added: Allow publishing from preview via link in admin bar. See [#115](https://github.com/xwp/wp-customize-snapshots/pull/115), [#103](https://github.com/xwp/wp-customize-snapshots/issues/103). +* Updated: Change link text for post list table action from “Edit” to “Inspect”. See [#155](https://github.com/xwp/wp-customize-snapshots/pull/155), [#153](https://github.com/xwp/wp-customize-snapshots/issues/153). +* Fixed: Prevent changeset session remembrance when browsing in preview iframe. See [#154](https://github.com/xwp/wp-customize-snapshots/pull/154). +* Added: Include required PHP version (5.3) in readme. See [#160](https://github.com/xwp/wp-customize-snapshots/pull/160), [#159](https://github.com/xwp/wp-customize-snapshots/issues/159). + +See [issues and PRs in milestone](https://github.com/xwp/wp-customize-snapshots/milestone/11?closed=1). + = 0.6.2 - 2017-07-26 = * Added: Just like the admin menu has Changesets link under Customize, add Changesets link in admin bar submenu item under Customize. See #143. From 54059d282943a1fa490ffbf1bbbe2d6a20b98ed9 Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Thu, 2 Nov 2017 17:09:07 +0530 Subject: [PATCH 40/90] Add changeset title in publish settings section --- css/customize-snapshots.css | 5 +- js/customize-snapshots.js | 64 +++++++++++++++++++++++- php/class-customize-snapshot-manager.php | 1 + 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css index b676616c..23bb32b4 100644 --- a/css/customize-snapshots.css +++ b/css/customize-snapshots.css @@ -1 +1,4 @@ -/* @todo Add any required styles for 4.9 here. */ + +#customize-control-changeset_title { + margin-top: 15px; +} \ No newline at end of file diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index 0cd2fa9b..294070aa 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -1,5 +1,5 @@ -/* global wp */ -/* eslint no-magic-numbers: [ "error", { "ignore": [0,1,-1] } ], consistent-this: [ "error", "snapshot" ] */ +/* global wp, $ */ +/* eslint consistent-this: [ "error", "snapshot" ] */ (function( api ) { 'use strict'; @@ -20,11 +20,71 @@ _.extend( snapshot.data, snapshotsConfig ); } + _.bindAll( snapshot, 'addTitleControl' ); + api.bind( 'ready', function() { // @todo Add snapshot-exists, snapshot-saved, snapshot-submitted states for back-compat? Skip if they are not used. + snapshot.spinner = $( '#customize-header-actions' ).find( '.spinner' ); + snapshot.saveBtn = $( '#save' ); + + snapshot.data.uuid = snapshot.data.uuid || api.settings.changeset.uuid; + snapshot.data.title = snapshot.data.title || snapshot.data.uuid; + api.state.create( 'changesetTitle', snapshot.data.title ); + + api.section( 'publish_settings', snapshot.addTitleControl ); api.trigger( 'snapshots-ready', snapshot ); } ); + }, + + /** + * Add title control to publish settings section. + * + * @param {wp.customize.Section} section Publish settings section. + * @return {void} + */ + addTitleControl: function( section ) { + var snapshot = this, control, toggleControl, originalQuery; + + control = new api.Control( 'changeset_title', { + type: 'text', + label: snapshot.data.i18n.title, + section: section.id, + setting: api.state( 'changesetTitle' ), + priority: 31 + } ); + + api.control.add( control ); + + toggleControl = function( status ) { + var activate = 'publish' !== status; + control.active.validate = function() { + return activate; + }; + control.active.set( activate ); + }; + + toggleControl( api.state( 'selectedChangesetStatus' ).get() ); + api.state( 'selectedChangesetStatus' ).bind( toggleControl ); + + originalQuery = api.previewer.query; + + api.previewer.query = function() { + var retval = originalQuery.apply( this, arguments ); + retval.customize_changeset_title = api.state( 'changesetTitle' ); + return retval; + }; + + api.state( 'changesetTitle' ).bind( function() { + api.state( 'saved' ).set( false ); + } ); + + $( window ).on( 'beforeunload.customize-confirm', function() { + if ( ! api.state( 'saved' ).get() ) { + return snapshot.data.i18n.aysMsg; + } + return undefined; + } ); } } ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index d6b43c20..ef7358be 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -264,6 +264,7 @@ public function enqueue_controls_scripts() { 'save' => __( 'You do not have permission to publish changes, but you can create a changeset by clicking the "Save" button.', 'customize-snapshots' ), 'update' => __( 'You do not have permission to publish changes, but you can modify this changeset by clicking the "Update" button.', 'customize-snapshots' ), ), + 'title' => __( 'Title', 'customize-snapshots' ), 'aysMsg' => __( 'Changes that you made may not be saved.', 'customize-snapshots' ), 'errorMsg' => __( 'The changeset could not be saved.', 'customize-snapshots' ), 'errorTitle' => __( 'Error', 'customize-snapshots' ), From 0b5ef52b7ef856f1f7285d2e07b75fdf8234e34f Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Thu, 2 Nov 2017 17:44:02 +0530 Subject: [PATCH 41/90] WIP: Add state query vars while saving --- js/customize-snapshots.js | 68 +++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index 294070aa..5d734c00 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -32,6 +32,7 @@ snapshot.data.title = snapshot.data.title || snapshot.data.uuid; api.state.create( 'changesetTitle', snapshot.data.title ); + snapshot.extendPreviewerQuery(); api.section( 'publish_settings', snapshot.addTitleControl ); api.trigger( 'snapshots-ready', snapshot ); } ); @@ -44,7 +45,7 @@ * @return {void} */ addTitleControl: function( section ) { - var snapshot = this, control, toggleControl, originalQuery; + var snapshot = this, control, toggleControl; control = new api.Control( 'changeset_title', { type: 'text', @@ -67,14 +68,6 @@ toggleControl( api.state( 'selectedChangesetStatus' ).get() ); api.state( 'selectedChangesetStatus' ).bind( toggleControl ); - originalQuery = api.previewer.query; - - api.previewer.query = function() { - var retval = originalQuery.apply( this, arguments ); - retval.customize_changeset_title = api.state( 'changesetTitle' ); - return retval; - }; - api.state( 'changesetTitle' ).bind( function() { api.state( 'saved' ).set( false ); } ); @@ -85,6 +78,63 @@ } return undefined; } ); + }, + + /** + * Amend the preview query so we can update the snapshot during `changeset_save`. + * + * @return {void} + */ + extendPreviewerQuery: function extendPreviewerQuery() { + var snapshot = this, originalQuery = api.previewer.query; + + api.previewer.query = function() { + var retval = originalQuery.apply( this, arguments ); + + if ( api.state( 'selectedChangesetStatus' ) && 'publish' !== api.state( 'selectedChangesetStatus' ) ) { + retval.customizer_state_query_vars = JSON.stringify( snapshot.getStateQueryVars() ); + retval.customize_changeset_title = api.state( 'changesetTitle' ); + } + + return retval; + }; + }, + + /** + * Get state query vars. + * @todo Reuse method in compat mode? + * + * @return {{}} Query vars for scroll, device, url, and autofocus. + */ + getStateQueryVars: function() { + var snapshot = this, queryVars; + + queryVars = { + 'autofocus[control]': null, + 'autofocus[section]': null, + 'autofocus[panel]': null + }; + + queryVars.scroll = parseInt( api.previewer.scroll, 10 ) || 0; + queryVars.device = api.previewedDevice.get(); + queryVars.url = api.previewer.previewUrl.get(); + + if ( ! api.state( 'activated' ).get() || snapshot.isNotSavedPreviewingTheme ) { + queryVars.previewing_theme = true; + } + + _.find( [ 'control', 'section', 'panel' ], function( constructType ) { + var found = false; + api[ constructType ].each( function( construct ) { // @todo Core needs to support more Backbone methods on wp.customize.Values(). + if ( ! found && construct.expanded && construct.expanded.get() ) { + queryVars[ 'autofocus[' + constructType + ']' ] = construct.id; + found = true; + } + } ); + return found; + } ); + + return queryVars; } } ); From 86722c8f3b9ba519ab89df7f02de54150c1bd63d Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Wed, 8 Nov 2017 11:32:03 +0530 Subject: [PATCH 42/90] Show countdown timer in schedule date --- css/customize-snapshots.css | 4 ++++ js/customize-snapshots.js | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css index 23bb32b4..6305d8b4 100644 --- a/css/customize-snapshots.css +++ b/css/customize-snapshots.css @@ -1,4 +1,8 @@ #customize-control-changeset_title { margin-top: 15px; +} + +.snapshot-countdown-container { + margin-top: 10px; } \ No newline at end of file diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index 5d734c00..2f2f0539 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -20,7 +20,7 @@ _.extend( snapshot.data, snapshotsConfig ); } - _.bindAll( snapshot, 'addTitleControl' ); + _.bindAll( snapshot, 'addTitleControl', 'setupScheduledChangesetCountdown' ); api.bind( 'ready', function() { // @todo Add snapshot-exists, snapshot-saved, snapshot-submitted states for back-compat? Skip if they are not used. @@ -33,6 +33,7 @@ api.state.create( 'changesetTitle', snapshot.data.title ); snapshot.extendPreviewerQuery(); + api.control( 'changeset_scheduled_date', snapshot.setupScheduledChangesetCountdown ); api.section( 'publish_settings', snapshot.addTitleControl ); api.trigger( 'snapshots-ready', snapshot ); } ); @@ -88,6 +89,7 @@ extendPreviewerQuery: function extendPreviewerQuery() { var snapshot = this, originalQuery = api.previewer.query; + // @todo See if can be done using 'save-request-params' event? api.previewer.query = function() { var retval = originalQuery.apply( this, arguments ); @@ -135,6 +137,30 @@ } ); return queryVars; + }, + + /** + * Setup scheduled changeset countdown. + * + * @param {wp.customize.Control} control Changeset schedule date control. + * @return {void} + */ + setupScheduledChangesetCountdown: function( control ) { + var template, countdownContainer; + + template = wp.template( 'snapshot-scheduled-countdown' ); + countdownContainer = $( '
', { + 'class': 'snapshot-countdown-container' + } ); + + control.deferred.embedded.done( function() { + control.container.append( countdownContainer ); + api.state( 'remainingTimeToPublish' ).bind( function( time ) { + countdownContainer.html( template( { + remainingTime: time + } ) ); + } ); + } ); } } ); From d471abf2501fbd017b5cfa1e75187250ca7e9209 Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Wed, 8 Nov 2017 12:04:35 +0530 Subject: [PATCH 43/90] Remove countdown when the changesetStatus changes. --- js/customize-snapshots.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index 2f2f0539..0eed062d 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -150,16 +150,22 @@ template = wp.template( 'snapshot-scheduled-countdown' ); countdownContainer = $( '
', { - 'class': 'snapshot-countdown-container' + 'class': 'snapshot-countdown-container hidden' } ); control.deferred.embedded.done( function() { control.container.append( countdownContainer ); api.state( 'remainingTimeToPublish' ).bind( function( time ) { - countdownContainer.html( template( { + countdownContainer.removeClass( 'hidden' ).html( template( { remainingTime: time } ) ); } ); + + api.state( 'changesetStatus' ).bind( function( status ) { + if ( 'future' !== status ) { + countdownContainer.addClass( 'hidden' ); + } + } ); } ); } } ); From 317527631a3ec441d7aa5c64a74cc6ae9e59563a Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Wed, 8 Nov 2017 15:00:36 +0530 Subject: [PATCH 44/90] Create compat class for snapshot manager and remove unused code for 4.9 --- ...lass-customize-snapshot-manager-compat.php | 1134 +++++++++++++++++ php/class-customize-snapshot-manager.php | 248 +--- php/class-plugin.php | 2 +- 3 files changed, 1140 insertions(+), 244 deletions(-) create mode 100644 php/class-customize-snapshot-manager-compat.php diff --git a/php/class-customize-snapshot-manager-compat.php b/php/class-customize-snapshot-manager-compat.php new file mode 100644 index 00000000..9d483c37 --- /dev/null +++ b/php/class-customize-snapshot-manager-compat.php @@ -0,0 +1,1134 @@ +post_type, 'init' ) ); + add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); + + add_action( 'load-edit.php', array( $this, 'handle_frontend_changeset_publish' ) ); + + add_action( 'customize_controls_init', array( $this, 'add_snapshot_uuid_to_return_url' ) ); + add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) ); + add_action( 'admin_bar_menu', array( $this, 'customize_menu' ), 41 ); + add_action( 'admin_bar_menu', array( $this, 'remove_all_non_snapshot_admin_bar_links' ), 100000 ); + add_action( 'wp_before_admin_bar_render', array( $this, 'print_admin_bar_styles' ) ); + add_filter( 'removable_query_args', array( $this, 'filter_removable_query_args' ) ); + add_action( 'save_post_' . $this->get_post_type(), array( $this, 'create_initial_changeset_revision' ) ); + add_action( 'save_post_' . $this->get_post_type(), array( $this, 'save_customizer_state_query_vars' ) ); + add_filter( 'wp_insert_post_data', array( $this, 'prepare_snapshot_post_content_for_publish' ) ); + remove_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' ); + add_action( 'delete_post', array( $this, 'clean_up_nav_menus_created_auto_drafts' ) ); + add_filter( 'customize_save_response', array( $this, 'add_snapshot_var_to_customize_save' ), 10, 2 ); + } + + /** + * Init. + */ + function init() { + $this->post_type = new Post_Type( $this ); + $this->hooks(); + } + + /** + * Add extra changeset variable to publish date. + * + * @param array $response Ajax response. + * @param \WP_Customize_Manager $customize_manager customize manager object. + * + * @return array Response. + */ + public function add_snapshot_var_to_customize_save( $response, $customize_manager ) { + $changeset_post = get_post( $customize_manager->changeset_post_id() ); + $response['edit_link'] = $this->get_edit_link( $changeset_post->ID ); + $response['publish_date'] = $changeset_post->post_date; + $response['title'] = $changeset_post->post_title; + return $response; + } + + /** + * Get edit post link. + * + * @param int|\WP_Post $post_id Post. + * + * @return null|string Post edit link. + */ + public function get_edit_link( $post_id ) { + $has_filter = has_filter( 'get_edit_post_link', '__return_empty_string' ); + if ( $has_filter ) { + remove_filter( 'get_edit_post_link', '__return_empty_string' ); + } + $link = get_edit_post_link( $post_id, 'raw' ); + if ( $has_filter ) { + add_filter( 'get_edit_post_link', '__return_empty_string' ); + } + return $link; + } + + /** + * Return true if it's a customize_save Ajax request. + * + * @return bool True if it's an Ajax request, false otherwise. + */ + public function doing_customize_save_ajax() { + return isset( $_REQUEST['action'] ) && sanitize_key( wp_unslash( $_REQUEST['action'] ) ) === 'customize_save'; // WPCS: input var ok. CSRF ok. + } + + + /** + * Ensure Customizer manager is instantiated. + * + * @global \WP_Customize_Manager $wp_customize + * @return \WP_Customize_Manager Manager. + */ + public function ensure_customize_manager() { + global $wp_customize; + + $args = array(); + if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { + require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); + $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. + } + + return $wp_customize; + } + + /** + * Is previewing another theme. + * + * @return bool Whether theme is active. + * + * @deprecated in favor of WP_Customize_Manager::is_theme_active() + */ + public function is_theme_active() { + return is_customize_preview() && $this->get_customize_manager()->is_theme_active(); + } + + /** + * Add snapshot UUID the Customizer return URL. + * + * If the Customizer was loaded from a referring URL had a changeset UUID, then ensure the return URL also includes this param. + */ + public function add_snapshot_uuid_to_return_url() { + $wp_customize = $this->get_customize_manager(); + $should_add_snapshot_uuid = ( + is_customize_preview() + && + false !== strpos( parse_url( wp_get_referer(), PHP_URL_QUERY ), 'customize_changeset_uuid=' . $wp_customize->changeset_uuid() ) + ); + if ( $should_add_snapshot_uuid ) { + $args_name = $this->get_front_uuid_param(); + $args = array( + $args_name => $wp_customize->changeset_uuid(), + ); + $return_url = add_query_arg( array_map( 'rawurlencode', $args ), $wp_customize->get_return_url() ); + $this->get_customize_manager()->set_return_url( $return_url ); + } + } + + /** + * Encode JSON with pretty formatting. + * + * @param array $value The snapshot value. + * @return string + */ + static public function encode_json( $value ) { + $flags = 0; + + $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. + * + * @action customize_controls_enqueue_scripts + * @global \WP_Customize_Manager $wp_customize + */ + public function enqueue_controls_scripts() { + wp_enqueue_style( 'customize-snapshots' ); + wp_enqueue_script( 'customize-snapshots' ); + + $post = null; + + $preview_url_query_vars = array(); + $post_id = $this->get_customize_manager()->changeset_post_id(); + if ( $post_id ) { + $post = get_post( $post_id ); + $preview_url_query_vars = $this->post_type->get_customizer_state_query_vars( $post->ID ); + if ( $post instanceof \WP_Post ) { + $this->override_post_date_default_data( $post ); + $edit_link = $this->get_edit_link( $post_id ); + } + } + + // Script data array. + $exports = apply_filters( 'customize_snapshots_export_data', array( + 'editLink' => isset( $edit_link ) ? $edit_link : '', + 'publishDate' => isset( $post->post_date ) ? $post->post_date : '', + 'title' => isset( $post->post_title ) ? $post->post_title : '', + 'postStatus' => isset( $post->post_status ) ? $post->post_status : '', + 'currentUserCanPublish' => current_user_can( 'customize_publish' ), + 'initialServerDate' => current_time( 'mysql', false ), + 'initialServerTimestamp' => floor( microtime( true ) * 1000 ), + 'previewingTheme' => isset( $preview_url_query_vars['theme'] ) ? $preview_url_query_vars['theme'] : '', + 'i18n' => array( + 'saveButton' => __( 'Save', 'customize-snapshots' ), + 'updateButton' => __( 'Update', 'customize-snapshots' ), + 'submit' => __( 'Submit', 'customize-snapshots' ), + 'submitted' => __( 'Submitted', 'customize-snapshots' ), + 'permsMsg' => array( + 'save' => __( 'You do not have permission to publish changes, but you can create a changeset by clicking the "Save" button.', 'customize-snapshots' ), + 'update' => __( 'You do not have permission to publish changes, but you can modify this changeset by clicking the "Update" button.', 'customize-snapshots' ), + ), + 'aysMsg' => __( 'Changes that you made may not be saved.', 'customize-snapshots' ), + 'errorMsg' => __( 'The changeset could not be saved.', 'customize-snapshots' ), + 'errorTitle' => __( 'Error', 'customize-snapshots' ), + 'collapseSnapshotScheduling' => __( 'Collapse changeset scheduling', 'customize-snapshots' ), + 'expandSnapshotScheduling' => __( 'Expand changeset scheduling', 'customize-snapshots' ), + ), + ) ); + + wp_scripts()->add_inline_script( + 'customize-snapshots', + sprintf( 'wp.customize.snapshots = new wp.customize.Snapshots( %s );', wp_json_encode( $exports ) ), + 'after' + ); + } + + /** + * Enqueue admin scripts. + * + * These files control the behavior and styling of links to remove settings. + * Published snapshots can't be edited, so these files are not needed on those pages. + * + * @param String $hook Current page in admin. + */ + public function enqueue_admin_scripts( $hook ) { + global $post; + $handle = 'customize-snapshots-admin'; + if ( ( 'post.php' === $hook ) && isset( $post->post_type ) && ( $this->get_post_type() === $post->post_type ) && ( 'publish' !== $post->post_status ) ) { + wp_enqueue_script( $handle ); + wp_enqueue_style( $handle ); + $exports = array( + 'deleteInputName' => $this->get_post_type() . '_remove_settings[]', + ); + wp_add_inline_script( + $handle, + sprintf( 'CustomizeSnapshotsAdmin.init( %s )', wp_json_encode( $exports ) ), + 'after' + ); + } + } + + /** + * Enqueue Customizer frontend scripts. + */ + public function enqueue_frontend_scripts() { + if ( ! is_customize_preview() || ! current_user_can( 'customize' ) ) { + return; + } + $handle = 'customize-snapshots-frontend'; + wp_enqueue_script( $handle ); + + $exports = array( + 'uuid' => $this->get_customize_manager()->changeset_uuid(), + 'home_url' => wp_parse_url( home_url( '/' ) ), + 'l10n' => array( + 'restoreSessionPrompt' => __( 'It seems you may have inadvertently navigated away from previewing a customized state. Would you like to restore the changeset context?', 'customize-snapshots' ), + ), + 'confirmationMsg' => __( 'Are you sure that you want to publish the Changeset?', 'customize-snapshots' ), + ); + wp_add_inline_script( + $handle, + sprintf( 'CustomizeSnapshotsFrontend.init( %s )', wp_json_encode( $exports ) ), + 'after' + ); + } + + /** + * Create initial changeset revision. + * + * This should be removed once #30854 is resolved. + * + * @link https://core.trac.wordpress.org/ticket/30854 + * + * @param int $post_id Post ID. + */ + public function create_initial_changeset_revision( $post_id ) { + if ( 0 === count( wp_get_post_revisions( $post_id ) ) ) { + wp_save_post_revision( $post_id ); + } + } + + /** + * Prepare snapshot post content for publishing. + * + * Strips out publish_error from content, with it potentially being re-added + * in a secondary wp_update_post() call if any of the settings in the post + * were not able to be saved. + * + * @param array $data An array of slashed post data. + * @return array Post data. + */ + public function prepare_snapshot_post_content_for_publish( $data ) { + $is_publishing_snapshot = ( + isset( $data['post_type'] ) + && + $this->get_post_type() === $data['post_type'] + && + 'publish' === $data['post_status'] + && + ( + empty( $data['ID'] ) + || + 'publish' !== get_post_status( $data['ID'] ) + ) + ); + if ( ! $is_publishing_snapshot ) { + return $data; + } + + $post_content = json_decode( wp_unslash( $data['post_content'] ), true ); + if ( ! is_array( $post_content ) ) { + return $data; + } + + // Remove publish_error from post_content. + foreach ( $post_content as $setting_id => &$setting_params ) { + if ( is_array( $setting_params ) ) { + unset( $setting_params['publish_error'] ); + } + } + + $data['post_content'] = wp_slash( self::encode_json( $post_content ) ); + + // @todo We could incorporate more of the logic from save_settings_with_publish_snapshot here to pre-emptively set the pending status. + return $data; + } + + /** + * Add snapshot_error_on_publish to removable_query_args. + * + * @param array $query_args Query args. + * @return array Removable query args. + */ + public function filter_removable_query_args( $query_args ) { + $query_args[] = 'snapshot_error_on_publish'; + return $query_args; + } + + /** + * Prepare a WP_Error for sending to JS. + * + * @param \WP_Error $error Error. + * @return array + */ + public function prepare_errors_for_response( \WP_Error $error ) { + $exported_errors = array(); + foreach ( $error->errors as $code => $messages ) { + $exported_errors[ $code ] = array( + 'message' => join( ' ', $messages ), + 'data' => $error->get_error_data( $code ), + ); + } + return $exported_errors; + } + + /** + * Generate a snapshot uuid. + * + * @return string + */ + static public function generate_uuid() { + return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), + mt_rand( 0, 0xffff ), + mt_rand( 0, 0x0fff ) | 0x4000, + mt_rand( 0, 0x3fff ) | 0x8000, + mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) + ); + } + + /** + * Determine whether the supplied UUID is in the right format. + * + * @param string $uuid Snapshot UUID. + * + * @return bool + */ + static public function is_valid_uuid( $uuid ) { + return 0 !== preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/', $uuid ); + } + + /** + * Toolbar modifications for Customize Snapshot + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function customize_menu( $wp_admin_bar ) { + add_action( 'wp_before_admin_bar_render', 'wp_customize_support_script' ); + $this->replace_customize_link( $wp_admin_bar ); + $this->add_changesets_admin_bar_link( $wp_admin_bar ); + $this->add_resume_snapshot_link( $wp_admin_bar ); + $this->add_post_edit_screen_link( $wp_admin_bar ); + $this->add_publish_changeset_link( $wp_admin_bar ); + $this->add_snapshot_exit_link( $wp_admin_bar ); + } + + /** + * Print admin bar styles. + */ + public function print_admin_bar_styles() { + // @codingStandardsIgnoreStart A WordPress-VIP sniff has false positive on admin bar being hidden. + ?> + + get_node( 'customize' ); + if ( empty( $customize_node ) ) { + return; + } + + // Remove customize_snapshot_uuid query param from url param to be previewed in Customizer. + $preview_url_query_params = array(); + $preview_url_parsed = wp_parse_url( $customize_node->href ); + parse_str( $preview_url_parsed['query'], $preview_url_query_params ); + if ( ! empty( $preview_url_query_params['url'] ) ) { + $preview_url_query_params['url'] = rawurlencode( remove_query_arg( array( $this->get_front_uuid_param() ), $preview_url_query_params['url'] ) ); + $customize_node->href = preg_replace( + '/(?<=\?).*?(?=#|$)/', + build_query( $preview_url_query_params ), + $customize_node->href + ); + } + + $wp_customize = $this->get_customize_manager(); + $args = array( + $this->get_customize_uuid_param() => $wp_customize->changeset_uuid(), + ); + + $post_id = $wp_customize->changeset_post_id(); + + if ( $post_id ) { + $customizer_state_query_vars = $this->post_type->get_customizer_state_query_vars( $post_id ); + unset( $customizer_state_query_vars['url'] ); + $args = array_merge( $args, $customizer_state_query_vars ); + } + + // Add customize_snapshot_uuid and preview url params to customize.php itself. + $customize_node->href = add_query_arg( $args, $customize_node->href ); + + $customize_node->meta['class'] .= ' ab-customize-snapshots-item'; + $wp_admin_bar->add_menu( (array) $customize_node ); + } + + /** + * Adds a link to resume snapshot previewing. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_changesets_admin_bar_link( $wp_admin_bar ) { + if ( ! $wp_admin_bar->get_node( 'customize' ) ) { + return; + } + $wp_admin_bar->add_node( array( + 'id' => 'customize-changesets', + 'parent' => 'customize', + 'title' => __( 'Changesets', 'customize-snapshots' ), + 'href' => admin_url( 'edit.php?post_type=customize_changeset' ), + 'meta' => array( + 'class' => 'ab-item', + ), + ) ); + } + + /** + * Adds a link to resume snapshot previewing. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_resume_snapshot_link( $wp_admin_bar ) { + $wp_admin_bar->add_menu( array( + 'id' => 'resume-customize-snapshot', + 'title' => __( 'Resume Changeset Preview', 'customize-snapshots' ), + 'href' => '#', + 'meta' => array( + 'class' => 'ab-item ab-customize-snapshots-item', + ), + ) ); + } + + /** + * Adds a "Inspect Changeset" link to the Toolbar when previewing a changeset. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_post_edit_screen_link( $wp_admin_bar ) { + if ( ! is_customize_preview() || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->edit_posts ) ) { + return; + } + $post_id = $this->get_customize_manager()->changeset_post_id(); + if ( ! $post_id ) { + return; + } + $wp_admin_bar->add_menu( array( + 'id' => 'inspect-customize-snapshot', + 'title' => __( 'Inspect Changeset', 'customize-snapshots' ), + 'href' => $this->get_edit_link( $post_id ), + 'meta' => array( + 'class' => 'ab-item ab-customize-snapshots-item', + ), + ) ); + } + + /** + * Adds a "Publish Changeset" link to the Toolbar when in Snapshot mode. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_publish_changeset_link( $wp_admin_bar ) { + if ( ! is_customize_preview() || ! current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) ) { + return; + } + + $wp_customize = $this->get_customize_manager(); + $post_id = $wp_customize->changeset_post_id(); + if ( ! $post_id ) { + return; + } + + $href = add_query_arg( + array( + 'post_type' => $this->post_type->get_slug(), + 'action' => 'frontend_publish', + 'uuid' => $wp_customize->changeset_uuid(), + 'stylesheet' => get_stylesheet(), + ), + admin_url( 'edit.php' ) + ); + $wp_admin_bar->add_menu( array( + 'id' => 'publish-customize-changeset', + 'title' => __( 'Publish Changeset', 'customize-snapshots' ), + 'href' => wp_nonce_url( $href, 'publish-changeset_' . $wp_customize->changeset_uuid() ), + 'meta' => array( + 'class' => 'ab-item ab-customize-snapshots-item', + ), + ) ); + } + + /** + * Adds an "Exit Changeset Preview" link to the Toolbar when previewing a changeset. + * + * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. + */ + public function add_snapshot_exit_link( $wp_admin_bar ) { + if ( ! is_customize_preview() ) { + return; + } + $wp_admin_bar->add_menu( array( + 'id' => 'exit-customize-snapshot', + 'title' => __( 'Exit Changeset Preview', 'customize-snapshots' ), + 'href' => remove_query_arg( $this->get_front_uuid_param() ), + 'meta' => array( + 'class' => 'ab-item ab-customize-snapshots-item', + ), + ) ); + } + + /** + * Remove all admin bar nodes that have links and which aren't for snapshots. + * + * @param \WP_Admin_Bar $wp_admin_bar Admin bar. + */ + public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { + if ( ! is_customize_preview() || ! current_user_can( 'customize' ) ) { + return; + } + $snapshot_admin_bar_node_ids = array( + 'customize', + 'exit-customize-snapshot', + 'inspect-customize-snapshot', + 'publish-customize-changeset', + ); + foreach ( $wp_admin_bar->get_nodes() as $node ) { + if ( in_array( $node->id, $snapshot_admin_bar_node_ids, true ) || '#' === substr( $node->href, 0, 1 ) ) { + continue; + } + + $parsed_link_url = wp_parse_url( $node->href ); + $parsed_home_url = wp_parse_url( home_url( '/' ) ); + $is_external_link = ( + isset( $parsed_link_url['host'] ) && $parsed_link_url['host'] !== $parsed_home_url['host'] + || + isset( $parsed_link_url['path'] ) && 0 !== strpos( $parsed_link_url['path'], $parsed_home_url['path'] ) + || + ( ! isset( $parsed_link_url['query'] ) || ! preg_match( '#(^|&)customize_snapshot_uuid=#', $parsed_link_url['query'] ) ) + ); + if ( $is_external_link ) { + $wp_admin_bar->remove_node( $node->id ); + } + } + } + + /** + * Underscore (JS) templates for dialog windows. + */ + public function render_templates() { + $this->add_edit_box_template(); + ?> + + + + + + + + + + + + get_month_choices(); + ?> + + + + get_month_abbrev( $wp_locale->get_month( $i ) ); + + /* translators: 1: month number (01, 02, etc.), 2: month abbreviation */ + $months[ $i ]['text'] = sprintf( __( '%1$s-%2$s', 'customize-snapshots' ), $month_number, $month_text ); + $months[ $i ]['value'] = $month_number; + } + return array( + 'month_choices' => $months, + ); + } + + /** + * Override default date values to a post. + * + * @param \WP_Post $post Post. + * @return \WP_Post Object if the post data did not apply. + */ + public function override_post_date_default_data( \WP_Post &$post ) { + if ( ! is_array( $post ) ) { + // Make sure that empty dates are not used in case of setting invalidity. + $empty_date = '0000-00-00 00:00:00'; + if ( $empty_date === $post->post_date ) { + $post->post_date = current_time( 'mysql', false ); + } + if ( $empty_date === $post->post_date_gmt ) { + $post->post_date_gmt = current_time( 'mysql', true ); + } + if ( $empty_date === $post->post_modified ) { + $post->post_modified = current_time( 'mysql', false ); + } + if ( $empty_date === $post->post_modified_gmt ) { + $post->post_modified_gmt = current_time( 'mysql', true ); + } + } + return $post; + } + + /** + * Get Post_Type from dynamic class. + * + * @return string Post type. + */ + public function get_post_type() { + return Post_Type::SLUG; + } + + /** + * Get Frontend UUID param. + * + * @return string param. + */ + public function get_front_uuid_param() { + return Post_Type::FRONT_UUID_PARAM_NAME; + } + + /** + * Get customize uuid param name. + * + * @return string customize param name. + */ + public function get_customize_uuid_param() { + return Post_Type::CUSTOMIZE_UUID_PARAM_NAME; + } + + /** + * Handles request to publish changeset from frontend. + */ + public function handle_frontend_changeset_publish() { + + if ( ! isset( $_GET['uuid'] ) || ! isset( $_GET['action'] ) || 'frontend_publish' !== $_GET['action'] ) { + return; + } + $uuid = sanitize_key( wp_unslash( $_GET['uuid'] ) ); + if ( ! static::is_valid_uuid( $uuid ) ) { + return; + } + + $is_user_authorized = ( + check_ajax_referer( 'publish-changeset_' . $uuid, false, false ) + && + current_user_can( get_post_type_object( $this->post_type->get_slug() )->cap->publish_posts ) + ); + if ( ! $is_user_authorized ) { + wp_die( + esc_html__( 'Oops. Unable to publish the changeset due to an expired user session. Please go back, reload the page, and try publishing again.', 'customize-snapshots' ), + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + 'response' => 401, + ) + ); + } + + $stylesheet = null; + if ( isset( $_GET['stylesheet'] ) ) { + $theme = wp_get_theme( wp_unslash( $_GET['stylesheet'] ) ); + if ( $theme->errors() ) { + $msg = __( 'Oops. Unable to publish the changeset. The following error(s) occurred: ', 'customize-snapshots' ); + $msg .= join( '; ', array_keys( $theme->errors()->errors ) ); + wp_die( + '

' . esc_html( $msg ) . '

', + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + 'response' => 400, + ) + ); + } + $stylesheet = $theme->get_stylesheet(); + } + + $wp_customize = $this->get_customize_manager(); + $args = array(); + if ( empty( $wp_customize ) || ! ( $wp_customize instanceof \WP_Customize_Manager ) ) { + require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); + $args['changeset_uuid'] = $uuid; + if ( $stylesheet ) { + $args['theme'] = $stylesheet; + } + $wp_customize = new \WP_Customize_Manager( $args ); // WPCS: override ok. + } + $r = $wp_customize->save_changeset_post( array( + 'status' => 'publish', + ) ); + + if ( is_wp_error( $r ) ) { + $msg = __( 'Oops. Unable to publish the changeset. The following error(s) occurred: ', 'customize-snapshots' ); + $msg .= join( '; ', array_keys( $r->errors ) ); + wp_die( + '

' . esc_html( $msg ) . '

', + esc_html__( 'Changeset publishing failed', 'customize-snapshots' ), + array( + 'back_link' => true, + 'response' => 500, + ) + ); + } else { + $referer = wp_get_referer(); + + // Ensure redirect is set to frontend. + if ( empty( $referer ) || false !== strpos( parse_url( $referer, PHP_URL_PATH ), '/wp-admin/' ) ) { + $referer = home_url(); + } + + $sendback = remove_query_arg( array( + $this->get_front_uuid_param(), + 'customize_theme', + $this->get_customize_uuid_param(), + ), $referer ); + + wp_redirect( $sendback ); + exit(); + } + } + + /** + * Save the preview url query vars in changeset meta. + * + * @param int $post_id Post id. + */ + public function save_customizer_state_query_vars( $post_id ) { + if ( ! isset( $_POST['customizer_state_query_vars'] ) ) { + return; + } + + $original_query_vars = json_decode( wp_unslash( $_POST['customizer_state_query_vars'] ), true ); + + if ( empty( $original_query_vars ) || ! is_array( $original_query_vars ) ) { + return; + } + + $this->post_type->set_customizer_state_query_vars( $post_id, $original_query_vars ); + } + + /** + * Clean up auto-draft post created by Nav menus on changeset delete. + * + * @param int $changeset_post_id Deleting changeset post id. + */ + public function clean_up_nav_menus_created_auto_drafts( $changeset_post_id ) { + global $wpdb; + $changeset_post = get_post( $changeset_post_id ); + + if ( ! ( $changeset_post instanceof \WP_Post ) || $changeset_post->post_type !== $this->get_post_type() ) { + return; + } + + $data = json_decode( $changeset_post->post_content, true ); + if ( empty( $data['nav_menus_created_posts']['value'] ) ) { + return; + } + remove_action( 'delete_post', array( $this, 'clean_up_nav_menus_created_auto_drafts' ) ); + foreach ( $data['nav_menus_created_posts']['value'] as $nav_menu_created_post_id ) { + if ( 'auto-draft' !== get_post_status( $nav_menu_created_post_id ) && 'draft' !== get_post_status( $nav_menu_created_post_id ) ) { + continue; + } + + /** + * If we have Customize post plugin then it will take care of post delete see: https://github.com/xwp/wp-customize-posts/pull/348 + * because it overrides nav_menus_created_posts data. + * + * @See WP_Customize_Posts:filter_out_nav_menus_created_posts_for_customized_posts() + */ + if ( ! class_exists( 'Customize_Posts_Plugin' ) ) { + // If customize post plugin is not installed we search for nav_menus_created_posts and lookup for reference via php code. + // Todo: Improve logic to find reference below. + $query = $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND ID != %d ", $this->get_post_type(), $changeset_post_id ); + $query .= $wpdb->prepare( ' AND post_content LIKE %s AND post_content LIKE %s LIMIT 50', '%' . $wpdb->esc_like( '"nav_menus_created_posts":' ) . '%', '%' . $nav_menu_created_post_id . '%' ); + $post_ids = $wpdb->get_col( $query ); // WPCS: unprepared SQL ok. + $should_delete = true; + if ( ! empty( $post_ids ) && is_array( $post_ids ) ) { + foreach ( $post_ids as $p_id ) { + $p = get_post( $p_id ); + if ( ! ( $p instanceof \WP_Post ) ) { + continue; + } + $content = json_decode( $p->post_content, true ); + if ( empty( $content['nav_menus_created_posts']['value'] ) ) { + continue; + } + if ( false !== array_search( $nav_menu_created_post_id, $content['nav_menus_created_posts']['value'] ) ) { + $should_delete = false; + break; + } + } + } + if ( $should_delete ) { + wp_delete_post( $nav_menu_created_post_id, true ); + } + } + } // End foreach(). + add_action( 'delete_post', array( $this, 'clean_up_nav_menus_created_auto_drafts' ) ); + } +} diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index ef7358be..8cf9de43 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -16,13 +16,6 @@ */ class Customize_Snapshot_Manager { - /** - * Action nonce. - * - * @type string - */ - const AJAX_ACTION = 'customize_update_snapshot'; - /** * Plugin instance. * @@ -244,22 +237,13 @@ public function enqueue_controls_scripts() { } } - // @todo Much of this is irrelevant as of 4.9. // Script data array. $exports = apply_filters( 'customize_snapshots_export_data', array( 'editLink' => isset( $edit_link ) ? $edit_link : '', - 'publishDate' => isset( $post->post_date ) ? $post->post_date : '', 'title' => isset( $post->post_title ) ? $post->post_title : '', - 'postStatus' => isset( $post->post_status ) ? $post->post_status : '', 'currentUserCanPublish' => current_user_can( 'customize_publish' ), - 'initialServerDate' => current_time( 'mysql', false ), - 'initialServerTimestamp' => floor( microtime( true ) * 1000 ), 'previewingTheme' => isset( $preview_url_query_vars['theme'] ) ? $preview_url_query_vars['theme'] : '', 'i18n' => array( - 'saveButton' => __( 'Save', 'customize-snapshots' ), - 'updateButton' => __( 'Update', 'customize-snapshots' ), - 'submit' => __( 'Submit', 'customize-snapshots' ), - 'submitted' => __( 'Submitted', 'customize-snapshots' ), 'permsMsg' => array( 'save' => __( 'You do not have permission to publish changes, but you can create a changeset by clicking the "Save" button.', 'customize-snapshots' ), 'update' => __( 'You do not have permission to publish changes, but you can modify this changeset by clicking the "Update" button.', 'customize-snapshots' ), @@ -268,8 +252,6 @@ public function enqueue_controls_scripts() { 'aysMsg' => __( 'Changes that you made may not be saved.', 'customize-snapshots' ), 'errorMsg' => __( 'The changeset could not be saved.', 'customize-snapshots' ), 'errorTitle' => __( 'Error', 'customize-snapshots' ), - 'collapseSnapshotScheduling' => __( 'Collapse changeset scheduling', 'customize-snapshots' ), - 'expandSnapshotScheduling' => __( 'Expand changeset scheduling', 'customize-snapshots' ), ), ) ); @@ -691,187 +673,13 @@ public function remove_all_non_snapshot_admin_bar_links( $wp_admin_bar ) { * Underscore (JS) templates for dialog windows. */ public function render_templates() { - $this->add_edit_box_template(); ?> - - - - - - - - - - get_month_choices(); - ?> - - get_month_abbrev( $wp_locale->get_month( $i ) ); - - /* translators: 1: month number (01, 02, etc.), 2: month abbreviation */ - $months[ $i ]['text'] = sprintf( __( '%1$s-%2$s', 'customize-snapshots' ), $month_number, $month_text ); - $months[ $i ]['value'] = $month_number; - } - return array( - 'month_choices' => $months, - ); - } - /** * Override default date values to a post. * diff --git a/php/class-plugin.php b/php/class-plugin.php index 52ac974b..539ec6db 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -52,7 +52,7 @@ public function __construct() { * @action after_setup_theme, 8 */ public function init() { - $this->customize_snapshot_manager = new Customize_Snapshot_Manager( $this ); + $this->customize_snapshot_manager = is_back_compat() ? new Customize_Snapshot_Manager_Compat( $this ) : new Customize_Snapshot_Manager( $this ); $this->customize_snapshot_manager->init(); } From 4c6d2c801b62e2b051cd6fc4a98835bf78ca9fa0 Mon Sep 17 00:00:00 2001 From: Sayed Taqui Date: Wed, 8 Nov 2017 23:23:14 +0530 Subject: [PATCH 45/90] Add edit post link to publish settings section [ci skip] --- css/customize-snapshots.css | 10 +++++ js/customize-snapshots.js | 51 ++++++++++++++++++++++-- php/class-customize-snapshot-manager.php | 4 ++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css index 6305d8b4..45dd7913 100644 --- a/css/customize-snapshots.css +++ b/css/customize-snapshots.css @@ -5,4 +5,14 @@ .snapshot-countdown-container { margin-top: 10px; +} + +.customize-control-edit-changeset-link { + width: auto; + float: right; + margin-top: -31px; +} + +.customize-control-edit-changeset-link .dashicons { + text-decoration: none; } \ No newline at end of file diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js index 0eed062d..0d27d6c9 100644 --- a/js/customize-snapshots.js +++ b/js/customize-snapshots.js @@ -1,5 +1,5 @@ /* global wp, $ */ -/* eslint consistent-this: [ "error", "snapshot" ] */ +/* eslint consistent-this: [ "error", "snapshot", "control" ] */ (function( api ) { 'use strict'; @@ -20,7 +20,7 @@ _.extend( snapshot.data, snapshotsConfig ); } - _.bindAll( snapshot, 'addTitleControl', 'setupScheduledChangesetCountdown' ); + _.bindAll( snapshot, 'setupScheduledChangesetCountdown' ); api.bind( 'ready', function() { // @todo Add snapshot-exists, snapshot-saved, snapshot-submitted states for back-compat? Skip if they are not used. @@ -31,10 +31,16 @@ snapshot.data.uuid = snapshot.data.uuid || api.settings.changeset.uuid; snapshot.data.title = snapshot.data.title || snapshot.data.uuid; api.state.create( 'changesetTitle', snapshot.data.title ); + api.state.create( 'changesetEditLink', snapshot.data.editLink ); snapshot.extendPreviewerQuery(); api.control( 'changeset_scheduled_date', snapshot.setupScheduledChangesetCountdown ); - api.section( 'publish_settings', snapshot.addTitleControl ); + + api.section( 'publish_settings', function( section ) { + snapshot.addTitleControl( section ); + snapshot.addEditChangesetControl( section ); + } ); + api.trigger( 'snapshots-ready', snapshot ); } ); }, @@ -167,6 +173,45 @@ } } ); } ); + }, + + /** + * Add edit changeset post link. + * + * @param {wp.customize.Section} section Section. + * @return {void} + */ + addEditChangesetControl: function( section ) { + var editLinkControl; + + editLinkControl = api.Control.extend( { + defaults: _.extend( {}, api.Control.prototype.defaults, { + templateId: 'snapshot-edit-link-control' + } ), + ready: function() { + var control = this, link; + link = control.container.find( 'a' ); + link.attr( 'href', control.setting() ); + control.setting.bind( function( value ) { + link.attr( 'href', value ); + } ); + + control.toggleEditLinkControl(); + api.state( 'changesetStatus' ).bind( function() { + control.toggleEditLinkControl(); + } ); + }, + toggleEditLinkControl: function() { + this.active.set( 'publish' !== api.state( 'changesetStatus' ).get() && api.state( 'changesetStatus' ).get() ); + } + } ); + + api.control.add( new editLinkControl( 'edit_changeset', { + type: 'edit-changeset-link', + section: section.id, + priority: 30, + setting: api.state( 'changesetEditLink' ) + } ) ); } } ); diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 8cf9de43..0b540af0 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -680,6 +680,10 @@ public function render_templates() {
+ + - + + - - -