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

Commit 6534def

Browse files
authored
Merge pull request #355 from xwp/fix/nested-post-field-partials
Fix post field partial from erroneously selecting nested post placements
2 parents d87ff82 + 02a177e commit 6534def

6 files changed

+131
-0
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
dist: precise
12
sudo: false
23

34
notifications:

js/customize-deferred-partial.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@
6060
}
6161
},
6262

63+
/**
64+
* Create and show the edit shortcut for a given partial placement container.
65+
*
66+
* Prevent multiple edit shortcuts from being created on the same placement.
67+
* This should probably be upstreamed to core.
68+
*
69+
* @param {Placement} placement The placement container element.
70+
* @returns {void}
71+
*/
72+
createEditShortcutForPlacement: function( placement ) {
73+
var partial = this;
74+
if ( placement.container && placement.container.find( '> .customize-partial-edit-shortcut-button:first-child' ).length ) {
75+
return;
76+
}
77+
api.selectiveRefresh.Partial.prototype.createEditShortcutForPlacement.call( partial, placement );
78+
},
79+
6380
/**
6481
* Request the new partial and render it into the placements.
6582
*

js/customize-post-field-partial.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,26 @@
167167
api.preview.send( 'focus-control', settingId + '[' + partial.params.field_id + ']' );
168168
},
169169

170+
/**
171+
* Find all placements for this partial in the document.
172+
*
173+
* Fixes issue where selector can match too many elements when post loops are nested inside other posts.
174+
*
175+
* @return {Array.<Placement>} Placements.
176+
*/
177+
placements: function() {
178+
var partial = this, placements;
179+
placements = api.selectiveRefresh.partialConstructor.deferred.prototype.placements.call( this );
180+
181+
// Remove all placements whose closest .hentry is not for this post.
182+
placements = _.filter( placements, function( placement ) {
183+
var closestHentry = placement.container.closest( '.hentry' );
184+
return ! closestHentry.length || closestHentry.hasClass( 'post-' + String( partial.params.post_id ) );
185+
});
186+
187+
return placements;
188+
},
189+
170190
/**
171191
* @inheritdoc
172192
*/

js/customize-preview-posts.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,75 @@
2020
}
2121
} );
2222

23+
/**
24+
* Parse the class name for an .hentry element, that is for an element that uses post_class().
25+
*
26+
* @param {string} className Class name.
27+
* @returns {object|null} Object with postType and postId props, or null if no matches.
28+
*/
29+
api.previewPosts.parsePostClassName = function parsePostClassName( className ) {
30+
var matches, postId, postType;
31+
matches = className.match( /(\s|^)post-(\d+)(\s|$)/ );
32+
if ( matches ) {
33+
postId = parseInt( matches[2], 10 );
34+
} else {
35+
return null;
36+
}
37+
matches = className.match( /(\s|^)type-(\S+)(\s|$)/ );
38+
if ( matches ) {
39+
postType = matches[2];
40+
} else {
41+
return null;
42+
}
43+
return {
44+
postId: postId,
45+
postType: postType
46+
};
47+
};
48+
49+
// Add partials when the document is modified and new post hentry elements are added.
50+
if ( 'undefined' !== typeof MutationObserver ) {
51+
api.previewPosts.mutationObserver = new MutationObserver( function( mutations ) {
52+
_.each( mutations, function( mutation ) {
53+
var hentryElements, mutationTarget;
54+
mutationTarget = $( mutation.target );
55+
hentryElements = mutationTarget.find( '.hentry' );
56+
if ( mutationTarget.is( '.hentry' ) ) {
57+
hentryElements = hentryElements.add( mutationTarget );
58+
}
59+
60+
hentryElements.each( function() {
61+
var postInfo, settingId;
62+
postInfo = api.previewPosts.parsePostClassName( $( this ).prop( 'className' ) );
63+
if ( ! postInfo ) {
64+
return;
65+
}
66+
settingId = 'post[' + postInfo.postType + '][' + String( postInfo.postId ) + ']';
67+
api.previewPosts.ensurePartialsForPostSetting( settingId );
68+
69+
// Ensure edit shortcuts are added for all placements inside the mutation target.
70+
_.each( api.previewPosts.partialSchema( settingId ), function( schema ) {
71+
var partial;
72+
partial = api.selectiveRefresh.partial( schema.id );
73+
if ( ! partial ) {
74+
return;
75+
}
76+
_.each( partial.placements(), function( placement ) { // eslint-disable-line max-nested-callbacks
77+
if ( mutationTarget.is( placement.container ) || $.contains( mutation.target, placement.container[0] ) ) {
78+
$( placement.container ).attr( 'title', api.selectiveRefresh.data.l10n.shiftClickToEdit );
79+
partial.createEditShortcutForPlacement( placement );
80+
}
81+
});
82+
});
83+
});
84+
});
85+
});
86+
api.previewPosts.mutationObserver.observe( document.documentElement, {
87+
childList: true,
88+
subtree: true
89+
});
90+
}
91+
2392
/**
2493
* Ensure that each post setting is added and has corresponding partials.
2594
*

php/class-wp-customize-posts-preview.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public function customize_preview_init() {
9292
add_filter( 'customize_dynamic_partial_args', array( $this, 'filter_customize_dynamic_partial_args' ), 10, 2 );
9393
add_filter( 'customize_dynamic_partial_class', array( $this, 'filter_customize_dynamic_partial_class' ), 10, 3 );
9494
add_filter( 'the_posts', array( $this, 'filter_the_posts_to_tally_previewed_posts' ), 1000 );
95+
add_action( 'the_post', array( $this, 'handle_the_post_to_tally_previewed_post' ) );
9596
add_filter( 'the_posts', array( $this, 'filter_the_posts_to_tally_orderby_keys' ), 10, 2 );
9697
add_action( 'wp_footer', array( $this, 'export_preview_data' ), 10 );
9798
add_filter( 'get_edit_post_link', array( $this, 'filter_get_edit_post_link' ), 10, 2 );
@@ -250,6 +251,15 @@ public function filter_the_posts_to_tally_previewed_posts( array $posts ) {
250251
return $posts;
251252
}
252253

254+
/**
255+
* Tally the set-up post as being previewed in the page.
256+
*
257+
* @param WP_Post $post Post.
258+
*/
259+
public function handle_the_post_to_tally_previewed_post( $post ) {
260+
$this->queried_post_ids[] = $post->ID;
261+
}
262+
253263
/**
254264
* Override post data for previewed settings.
255265
*

tests/php/test-class-wp-customize-posts-preview.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public function test_customize_preview_init() {
125125
$this->assertEquals( 10, has_filter( 'customize_dynamic_partial_args', array( $preview, 'filter_customize_dynamic_partial_args' ) ) );
126126
$this->assertEquals( 10, has_filter( 'customize_dynamic_partial_class', array( $preview, 'filter_customize_dynamic_partial_class' ) ) );
127127
$this->assertEquals( 1000, has_filter( 'the_posts', array( $preview, 'filter_the_posts_to_tally_previewed_posts' ) ) );
128+
$this->assertEquals( 10, has_action( 'the_post', array( $preview, 'handle_the_post_to_tally_previewed_post' ) ) );
128129
$this->assertEquals( 10, has_filter( 'the_posts', array( $preview, 'filter_the_posts_to_tally_orderby_keys' ) ) );
129130
$this->assertEquals( 10, has_action( 'wp_footer', array( $preview, 'export_preview_data' ) ) );
130131
$this->assertEquals( 10, has_filter( 'get_edit_post_link', array( $preview, 'filter_get_edit_post_link' ) ) );
@@ -277,6 +278,19 @@ public function test_filter_the_posts_to_tally_previewed_posts() {
277278
$this->assertEqualSets( $post_ids, $this->posts_component->preview->queried_post_ids );
278279
}
279280

281+
/**
282+
* Test handle_the_post_to_tally_previewed_post().
283+
*
284+
* @covers WP_Customize_Posts_Preview::handle_the_post_to_tally_previewed_post()
285+
*/
286+
public function test_handle_the_post_to_tally_previewed_post() {
287+
$post_id = $this->factory()->post->create();
288+
$this->assertEmpty( $this->posts_component->preview->queried_post_ids );
289+
$this->posts_component->preview->customize_preview_init();
290+
setup_postdata( $post_id );
291+
$this->assertEqualSets( array( $post_id ), $this->posts_component->preview->queried_post_ids );
292+
}
293+
280294
/**
281295
* Test filter_the_posts_to_preview_settings().
282296
*

0 commit comments

Comments
 (0)