Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

Improve logic for determining when to do fallback refresh for post field partials #335

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions js/customize-post-field-partial.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
api.selectiveRefresh.partialConstructor.deferred.prototype.initialize.call( partial, id, args );

partial.addInstantPreviews();

// @todo If singular_only, and this is not the post singular post for this partial, then no refresh!
},

/**
Expand Down Expand Up @@ -123,6 +121,41 @@
return refreshPromise;
},

/**
* Handle fail to render partial.
*
* {@inheritdoc}
*
* @this {wp.customize.selectiveRefresh.partialConstructor.deferred}
* @returns {void}
*/
fallback: function postFieldPartialFallback() {
var partial = this, dependentSelector;

/*
* Skip invoking fallback behavior for partials on documents that lack matches for
* the fallback dependent selector. The default fallback dependent selector is
* essentially checking to see if a body_class or post_class exists in the document
* which references the given post. If the dependent selector fails to match any
* elements, then the selector dependency fails and the partial should not be added.
* Note that the dependent selector could have been used as a determiner for whether
* the partial was added in the first place. However, this would have meant that no
* selective refresh requests would have been spawned by the change, and this would
* have meant that any Backbone models for the WP-API would not have had the chance
* to get the rendered updates from the server.
*/
dependentSelector = partial.params.fallbackDependentSelector;
if ( ! dependentSelector ) {
dependentSelector = '.hentry.post-%d, body.page-id-%d, body.postid-%d';
}
dependentSelector = dependentSelector.replace( /%d/g, String( partial.params.post_id ) );
if ( 0 === $( dependentSelector ).length ) {
return;
}

api.selectiveRefresh.partialConstructor.deferred.prototype.fallback.call( partial );
},

/**
* @inheritdoc
*/
Expand Down
69 changes: 60 additions & 9 deletions js/customize-preview-featured-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ var CustomizePreviewFeaturedImage = (function( api, $ ) {

var component = {
data: {
partialSelectorAttribute: '',
partialContainerInclusive: true
partialArgs: {
selector: '',
containerInclusive: true,
fallbackDependentSelector: ''
}
}
};

Expand Down Expand Up @@ -56,6 +59,37 @@ var CustomizePreviewFeaturedImage = (function( api, $ ) {
*/
component.FeaturedImagePartial = api.selectiveRefresh.partialConstructor.deferred.extend({

idPattern: /^postmeta\[(.+?)]\[(\d+)]\[_thumbnail_id]$/,

/**
* Initialize.
*
* @param {string} id Partial ID.
* @param {object} args Args.
* @param {object} args.params Params.
* @returns {void}
*/
initialize: function( id, args ) {
var partial = this, matches, postId, postType, params;
matches = id.match( partial.idPattern );
postType = matches[1];
postId = parseInt( matches[2], 10 );
params = _.extend(
{
post_id: postId,
post_type: postType,
selector: component.data.partialArgs.selector.replace( /%d/g, String( postId ) ),
settings: [ id ],
primarySetting: id,
containerInclusive: component.data.partialArgs.containerInclusive,
fallbackDependentSelector: component.data.partialArgs.fallbackDependentSelector
},
args ? args.params || {} : {}
);

api.selectiveRefresh.partialConstructor.deferred.prototype.initialize.call( partial, id, { params: params } );
},

/**
* Force fallback (full page refresh) behavior when the featured image is removed.
*
Expand Down Expand Up @@ -93,6 +127,27 @@ var CustomizePreviewFeaturedImage = (function( api, $ ) {
} else {
return api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement );
}
},

/**
* Handle fail to render partial.
*
* Skip performing fallback behavior if post does not appear on the current template.
*
* {@inheritdoc}
*
* @this {wp.customize.selectiveRefresh.partialConstructor.deferred}
* @returns {void}
*/
fallback: function postFieldPartialFallback() {
var partial = this, dependentSelector;

dependentSelector = partial.params.fallbackDependentSelector.replace( /%d/g, String( partial.params.post_id ) );
if ( 0 === $( dependentSelector ).length ) {
return;
}

api.selectiveRefresh.partialConstructor.deferred.prototype.fallback.call( partial );
}
});

Expand All @@ -103,22 +158,18 @@ var CustomizePreviewFeaturedImage = (function( api, $ ) {
* @returns {component.FeaturedImagePartial|null} New or existing featured image partial, or null if not relevant setting.
*/
component.ensurePartialForSetting = function ensurePartialForSetting( setting ) {
var ensuredPartial, partialId, postId, matches = setting.id.match( /^postmeta\[.+?]\[(\d+)]\[_thumbnail_id]$/ );
if ( ! matches ) {
var ensuredPartial, partialId;
if ( ! component.FeaturedImagePartial.prototype.idPattern.test( setting.id ) ) {
return null;
}
partialId = setting.id;
postId = parseInt( matches[1], 10 );
ensuredPartial = api.selectiveRefresh.partial( partialId );
if ( ensuredPartial ) {
return ensuredPartial;
}
ensuredPartial = new component.FeaturedImagePartial( partialId, {
params: {
selector: '[' + component.data.partialSelectorAttribute + '=' + String( postId ) + ']',
settings: [ setting.id ],
primarySetting: setting.id,
containerInclusive: component.data.partialContainerInclusive
settings: [ setting.id ]
}
} );
api.selectiveRefresh.partial.add( partialId, ensuredPartial );
Expand Down
37 changes: 20 additions & 17 deletions js/customize-preview-posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

// Add the partials.
_.each( api.previewPosts.partialSchema( setting.id ), function( schema ) {
var partial, addPartial, matches, baseSelector;
var partial, matches, postId, postType, selectorBases;

matches = schema.id.match( idPattern );
if ( ! matches ) {
Expand All @@ -46,25 +46,28 @@
if ( api.selectiveRefresh.partial.has( schema.id ) ) {
return;
}
postType = matches[1];
postId = parseInt( matches[2], 10 );

if ( schema.params.selector ) {
if ( ! schema.params.bodySelector ) {
baseSelector = '.hentry.post-' + String( parseInt( matches[2], 10 ) ) + '.type-' + matches[1];

selectorBases = [
'.hentry.post-' + String( postId )
];
if ( 'page' === postType ) {
selectorBases.push( 'body.page.page-id-' + String( postId ) );
} else {
baseSelector = '.postid-' + String( parseInt( matches[2], 10 ) ) + '.single-' + matches[1];
selectorBases.push( 'body.postid-' + String( postId ) );
}
schema.params.selector = baseSelector + ' ' + schema.params.selector;

addPartial =
! schema.params.singularOnly && ! schema.params.archiveOnly ||
schema.params.singularOnly && api.previewPosts.data.isSingular ||
schema.params.archiveOnly && ! api.previewPosts.data.isSingular;
schema.params.selector = _.map( selectorBases, function( selectorBase ) {
var selector = selectorBase + ' ' + schema.params.selector;
selector = selector.replace( /%d/g, String( postId ) );
return selector;
} ).join( ', ' );

if ( addPartial ) {
partial = new api.selectiveRefresh.partialConstructor.post_field( schema.id, { params: schema.params } );
api.selectiveRefresh.partial.add( partial.id, partial );
addedPartials.push( partial );
}
partial = new api.selectiveRefresh.partialConstructor.post_field( schema.id, { params: schema.params } );
api.selectiveRefresh.partial.add( partial.id, partial );
addedPartials.push( partial );
} else {
partial = new api.selectiveRefresh.partialConstructor.post_field( schema.id, { params: schema.params } );

Expand All @@ -79,7 +82,7 @@
partial.refresh = function refreshWithoutSelector() {
var deferred = $.Deferred();
if ( this.params.fallbackRefresh ) {
api.selectiveRefresh.requestFullRefresh();
api.selectiveRefresh.requestFullRefresh(); // @todo Do partial.fallback()?
deferred.resolve();
} else {
deferred.reject();
Expand Down Expand Up @@ -209,7 +212,7 @@
} );
};

wp.customize.selectiveRefresh.bind( 'render-partials-response', api.previewPosts.handleRenderPartialsResponse );
api.selectiveRefresh.bind( 'render-partials-response', api.previewPosts.handleRenderPartialsResponse );
};

/**
Expand Down
18 changes: 15 additions & 3 deletions php/class-wp-customize-featured-image-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ class WP_Customize_Featured_Image_Controller extends WP_Customize_Postmeta_Contr
*/
const SELECTED_ATTRIBUTE = 'data-customize-featured-image-partial';

/**
* Selector for finding featured images.
*
* @var string
*/
const SELECTOR = '[data-customize-featured-image-partial="%d"]';

/**
* The container_inclusive param for the partials.
*
Expand Down Expand Up @@ -117,9 +124,14 @@ public function enqueue_customize_pane_scripts() {
public function enqueue_customize_preview_scripts() {
$handle = 'customize-preview-featured-image';
wp_enqueue_script( $handle );

// @todo These arguments should be configurable for featured image partials just as they are for post field partials.
$exports = array(
'partialSelectorAttribute' => self::SELECTED_ATTRIBUTE,
'partialContainerInclusive' => self::PARTIAL_CONTAINER_INCLUSIVE,
'partialArgs' => array(
'selector' => self::SELECTOR,
'fallbackDependentSelector' => '.hentry.post-%d, body.page-id-%d, body.postid-%d',
'containerInclusive' => self::PARTIAL_CONTAINER_INCLUSIVE,
),
);
wp_add_inline_script( $handle, sprintf( 'CustomizePreviewFeaturedImage.init( %s )', wp_json_encode( $exports ) ) );
}
Expand Down Expand Up @@ -281,7 +293,7 @@ public function filter_customize_dynamic_partial_args( $partial_args, $partial_i
$partial_args['settings'] = array( $setting_id );
$partial_args['primary_setting'] = $setting_id;
$partial_args['type'] = 'featured_image';
$partial_args['selector'] = '[' . self::SELECTED_ATTRIBUTE . '=' . $matches['post_id'] . ']';
$partial_args['selector'] = sprintf( self::SELECTOR, $matches['post_id'] );
$partial_args['container_inclusive'] = self::PARTIAL_CONTAINER_INCLUSIVE;
}
return $partial_args;
Expand Down
31 changes: 11 additions & 20 deletions php/class-wp-customize-posts-preview.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ public function customize_preview_init() {
add_action( 'parse_query', array( $this, 'ensure_page_for_posts_preview' ), 5 );
add_filter( 'customize_dynamic_partial_args', array( $this, 'filter_customize_dynamic_partial_args' ), 10, 2 );
add_filter( 'customize_dynamic_partial_class', array( $this, 'filter_customize_dynamic_partial_class' ), 10, 3 );
add_filter( 'customize_posts_partial_schema', array( $this, 'filter_customize_posts_partial_schema' ) );
add_filter( 'the_posts', array( $this, 'filter_the_posts_to_tally_previewed_posts' ), 1000 );
add_filter( 'the_posts', array( $this, 'filter_the_posts_to_tally_orderby_keys' ), 10, 2 );
add_action( 'wp_footer', array( $this, 'export_preview_data' ), 10 );
Expand Down Expand Up @@ -1222,20 +1221,6 @@ function filter_customize_dynamic_partial_class( $partial_class, $partial_id, $p
return $partial_class;
}

/**
* Prevent fallback_refresh for select post fields.
*
* @todo There should be some more sophisticated logic for determining whether fallback_refresh is done.
*
* @param array $schema Schema.
* @return array Schema.
*/
function filter_customize_posts_partial_schema( $schema ) {
$schema['post_title']['fallback_refresh'] = false;
$schema['post_excerpt']['fallback_refresh'] = false;
return $schema;
}

/**
* Filters get_edit_post_link to short-circuits if post cannot be edited in Customizer.
*
Expand Down Expand Up @@ -1321,22 +1306,19 @@ public function get_post_field_partial_schema( $field_id = '' ) {
),
'post_excerpt' => array(
'selector' => '.entry-summary',
'fallback_refresh' => true,
),
'comment_status[comments-area]' => array(
'selector' => '.comments-area',
'body_selector' => true,
'singular_only' => true,
'container_inclusive' => true,
),
'comment_status[comments-link]' => array(
'selector' => '.comments-link',
'archive_only' => true,
'fallback_dependent_selector' => 'body.archive', // Only do fallback when on archives.
'container_inclusive' => true,
),
'ping_status' => array(
'selector' => '.comments-area',
'body_selector' => true,
'singular_only' => true,
'container_inclusive' => true,
),
'post_author[byline]' => array(
Expand All @@ -1359,6 +1341,15 @@ public function get_post_field_partial_schema( $field_id = '' ) {
*/
$schema = apply_filters( 'customize_posts_partial_schema', $schema );

$deprecated_keys = array( 'body_selector', 'archive_only', 'singular_only' );
foreach ( $schema as $_field_id => $_partial_args ) {
foreach ( $deprecated_keys as $deprecated_key ) {
if ( array_key_exists( $deprecated_key, $_partial_args ) ) {
_deprecated_argument( __FUNCTION__, '0.8.5', sprintf( __( 'The %s param has been removed from the partial schema. Consider fallback_dependent_selector if needed.', 'customize-posts' ), $deprecated_key ) );
}
}
}

// Return specific schema based on the field_id & placement.
if ( ! empty( $field_id ) ) {
if ( isset( $schema[ $field_id ] ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function filter_partial_schema( $schema ) {

$schema['post_author[biography]'] = array(
'selector' => '#author-info',
'singular_only' => true,
'fallback_dependent_selector' => 'body.singular',
'container_inclusive' => true,
'render_callback' => array( $this, 'biography_render_callback' ),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function add_support() {
public function filter_partial_schema( $schema ) {
$schema['post_author[biography]'] = array(
'selector' => '.author-info',
'singular_only' => true,
'fallback_dependent_selector' => 'body.singular',
'container_inclusive' => true,
'render_callback' => array( $this, 'biography_render_callback' ),
);
Expand Down
Loading