@automattic/newspack-blocks 4.31.0 → 4.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## @automattic/newspack-blocks [4.31.2](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.31.1...newspack-blocks@4.31.2) (2026-09-10)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **iframe:** keep Back working on iframe block pages (NPPM-3180) ([#1042](https://github.com/Automattic/newspack-workspace/issues/1042)) ([6a87cb2](https://github.com/Automattic/newspack-workspace/commit/6a87cb2c6dcab735a81b54e87fb11b93769baac1))
7
+
8
+ ## @automattic/newspack-blocks [4.31.1](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.31.0...newspack-blocks@4.31.1) (2026-09-09)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **homepage-articles:** reset dedup state per render pass (NPLAUNC-242) ([#1019](https://github.com/Automattic/newspack-workspace/issues/1019)) ([0ecdc9b](https://github.com/Automattic/newspack-workspace/commit/0ecdc9b7e9511799df668738a732ef6f0d808cb4))
14
+
1
15
  # @automattic/newspack-blocks [4.31.0](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.30.5...newspack-blocks@4.31.0) (2026-08-31)
2
16
 
3
17
 
@@ -1 +1 @@
1
- <?php return array('dependencies' => array('wp-dom-ready'), 'version' => 'b237b52924e68e8bf7a5');
1
+ <?php return array('dependencies' => array('wp-dom-ready'), 'version' => '0adcb9c939693718d723');
@@ -1 +1 @@
1
- (()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.domReady;e.n(t)()(()=>{Array.from(document.querySelectorAll(".wp-block-newspack-blocks-iframe iframe")).forEach(e=>{const t=setInterval(function(){e.src=e.src},2e3);e.onload=function(){clearInterval(t)},window.addEventListener("message",function(t){if(t.origin!==new URL(e.src).origin||e.contentWindow!==t.source)return;let n=0;t.data&&t.data.height&&("number"==typeof t.data.height?n=t.data.height:"string"==typeof t.data.height&&(n=Number(t.data.height))),!isNaN(n)&&n>0&&(e.parentElement&&"auto"!==e.parentElement.style.height&&(e.parentElement.style.height="auto"),e.style.height=n+"px")})})})})();
1
+ (()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.domReady;e.n(t)()(()=>{Array.from(document.querySelectorAll(".wp-block-newspack-blocks-iframe iframe")).forEach(e=>{let t=0;const n=setInterval(()=>{!(e=>{const t=e.contentDocument;return null!==t&&"about:blank"===t.URL})(e)||t>=10?clearInterval(n):(t++,e.contentWindow.location.replace(e.src))},2e3);window.addEventListener("message",function(t){if(t.origin!==new URL(e.src).origin||e.contentWindow!==t.source)return;let n=0;t.data&&t.data.height&&("number"==typeof t.data.height?n=t.data.height:"string"==typeof t.data.height&&(n=Number(t.data.height))),!isNaN(n)&&n>0&&(e.parentElement&&"auto"!==e.parentElement.style.height&&(e.parentElement.style.height="auto"),e.style.height=n+"px")})})})})();
@@ -20,6 +20,14 @@ class Newspack_Blocks {
20
20
  'tiers-based' => 'newspack-blocks-donate-tiers-based',
21
21
  ];
22
22
 
23
+ /**
24
+ * Nesting depth of `the_content` filter applications. A depth of zero marks
25
+ * the start of a top-level render pass, where deduplication state is reset.
26
+ *
27
+ * @var int
28
+ */
29
+ private static $content_render_depth = 0;
30
+
23
31
  /**
24
32
  * Add hooks and filters.
25
33
  */
@@ -30,6 +38,8 @@ class Newspack_Blocks {
30
38
  add_post_type_support( 'page', 'newspack_blocks' );
31
39
  add_action( 'jetpack_register_gutenberg_extensions', [ __CLASS__, 'disable_jetpack_donate' ], 99 );
32
40
  add_filter( 'the_content', [ __CLASS__, 'hide_post_content_when_iframe_block_is_fullscreen' ] );
41
+ add_filter( 'the_content', [ __CLASS__, 'start_content_render_pass' ], PHP_INT_MIN );
42
+ add_filter( 'the_content', [ __CLASS__, 'end_content_render_pass' ], PHP_INT_MAX );
33
43
  add_filter( 'body_class', [ __CLASS__, 'add_body_classes' ] );
34
44
  add_filter( 'admin_body_class', [ __CLASS__, 'add_body_classes' ] );
35
45
 
@@ -616,6 +626,73 @@ class Newspack_Blocks {
616
626
  return false;
617
627
  }
618
628
 
629
+ /**
630
+ * Mark the start of a `the_content` render pass.
631
+ *
632
+ * Deduplication state accumulates in globals for the life of the request,
633
+ * which is right for a front-end page but wrong wherever one request renders
634
+ * the same content more than once (a REST save renders it up to three times)
635
+ * or renders several posts (a REST collection). There, every top-level pass
636
+ * starts from a clean slate so each returns the same posts. Nested passes
637
+ * (a Query Loop rendering Post Content, a synced pattern) keep the state of
638
+ * the pass they belong to.
639
+ *
640
+ * @param string $content Post content.
641
+ * @return string Unmodified post content.
642
+ */
643
+ public static function start_content_render_pass( $content ) {
644
+ if ( 0 === self::$content_render_depth && self::should_reset_deduplication_per_render_pass() ) {
645
+ self::reset_deduplication();
646
+ }
647
+ self::$content_render_depth++;
648
+ return $content;
649
+ }
650
+
651
+ /**
652
+ * Mark the end of a `the_content` render pass.
653
+ *
654
+ * @param string $content Post content.
655
+ * @return string Unmodified post content.
656
+ */
657
+ public static function end_content_render_pass( $content ) {
658
+ self::$content_render_depth = max( 0, self::$content_render_depth - 1 );
659
+ return $content;
660
+ }
661
+
662
+ /**
663
+ * Whether deduplication state should be reset at the start of each top-level
664
+ * `the_content` pass.
665
+ *
666
+ * On the front end the state is kept for the whole request, because a block
667
+ * theme can render Content Loop blocks in the template before the post
668
+ * content, and those have to be excluded from the blocks inside it.
669
+ *
670
+ * @return bool
671
+ */
672
+ public static function should_reset_deduplication_per_render_pass() {
673
+ $is_front_end = ! is_admin()
674
+ && ! wp_doing_ajax()
675
+ && ! wp_doing_cron()
676
+ && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
677
+ && ! ( defined( 'WP_CLI' ) && WP_CLI );
678
+
679
+ return ! $is_front_end;
680
+ }
681
+
682
+ /**
683
+ * Forget which posts have been rendered so far, so the next Content Loop or
684
+ * Carousel block starts deduplicating from scratch.
685
+ *
686
+ * The list of posts picked by specific-posts blocks is left alone: it is
687
+ * derived from the current post, which `wp_reset_postdata()` cannot restore
688
+ * inside a REST request once a block's loop has moved it, so recomputing it
689
+ * per pass would give each pass a different exclusion list.
690
+ */
691
+ public static function reset_deduplication() {
692
+ global $newspack_blocks_post_id;
693
+ $newspack_blocks_post_id = [];
694
+ }
695
+
619
696
  /**
620
697
  * Whether the block should be included in the deduplication logic.
621
698
  *
@@ -712,6 +789,8 @@ class Newspack_Blocks {
712
789
  'has_password' => false,
713
790
  'is_newspack_query' => true,
714
791
  'tax_query' => [], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
792
+ // The total is only needed to decide whether a More button has a next page.
793
+ 'no_found_rows' => empty( $attributes['moreButton'] ),
715
794
  );
716
795
  if ( $specific_mode && $specific_posts ) {
717
796
  $args['posts_per_page'] = count( $specific_posts );
@@ -2,7 +2,7 @@ msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Newspack Blocks\n"
4
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
5
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
5
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
6
6
  "PO-Revision-Date: 2024-08-30 08:45-0700\n"
7
7
  "Last-Translator: \n"
8
8
  "Language-Team: \n"
@@ -282,45 +282,45 @@ msgstr ""
282
282
  msgid "Newspack Subscribe"
283
283
  msgstr ""
284
284
 
285
- #: includes/class-newspack-blocks.php:983
285
+ #: includes/class-newspack-blocks.php:1062
286
286
  msgid "Common"
287
287
  msgstr ""
288
288
 
289
289
  #. translators: separates last two sponsor names; needs a space on either side.
290
- #: includes/class-newspack-blocks.php:1084
290
+ #: includes/class-newspack-blocks.php:1163
291
291
  msgid " and "
292
292
  msgstr " und "
293
293
 
294
294
  #. translators: separates all but the last two sponsor names; needs a space at the end.
295
- #: includes/class-newspack-blocks.php:1087
295
+ #: includes/class-newspack-blocks.php:1166
296
296
  msgid ", "
297
297
  msgstr ""
298
298
 
299
- #: includes/class-newspack-blocks.php:1422
299
+ #: includes/class-newspack-blocks.php:1501
300
300
  msgid "Jetpack donations is disabled in favour of Newspack donations."
301
301
  msgstr ""
302
302
 
303
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:26
303
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:26
304
304
  #: src/shared/js/utils.js:54
305
305
  msgid "Draft"
306
306
  msgstr ""
307
307
 
308
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:26
308
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:26
309
309
  #: src/shared/js/utils.js:54
310
310
  msgid "Scheduled"
311
311
  msgstr ""
312
312
 
313
313
  #. Translators: %s is the %s is the frequency.
314
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:48
314
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:48
315
315
  #, php-format
316
316
  msgid "per %s"
317
317
  msgstr ""
318
318
 
319
- #: includes/class-newspack-blocks.php:1685
319
+ #: includes/class-newspack-blocks.php:1764
320
320
  msgid " once"
321
321
  msgstr ""
322
322
 
323
- #: includes/class-newspack-blocks.php:1688
323
+ #: includes/class-newspack-blocks.php:1767
324
324
  msgid " per "
325
325
  msgstr ""
326
326
 
@@ -2,7 +2,7 @@ msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Newspack Blocks 1.0.0-alpha.20\n"
4
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
5
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
5
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
6
6
  "PO-Revision-Date: 2025-08-12T14:56:45+00:00\n"
7
7
  "Last-Translator: \n"
8
8
  "Language-Team: \n"
@@ -274,45 +274,45 @@ msgstr ""
274
274
  msgid "Newspack Subscribe"
275
275
  msgstr ""
276
276
 
277
- #: includes/class-newspack-blocks.php:983
277
+ #: includes/class-newspack-blocks.php:1062
278
278
  msgid "Common"
279
279
  msgstr ""
280
280
 
281
281
  #. translators: separates last two sponsor names; needs a space on either side.
282
- #: includes/class-newspack-blocks.php:1084
282
+ #: includes/class-newspack-blocks.php:1163
283
283
  msgid " and "
284
284
  msgstr " y "
285
285
 
286
286
  #. translators: separates all but the last two sponsor names; needs a space at the end.
287
- #: includes/class-newspack-blocks.php:1087
287
+ #: includes/class-newspack-blocks.php:1166
288
288
  msgid ", "
289
289
  msgstr ""
290
290
 
291
- #: includes/class-newspack-blocks.php:1422
291
+ #: includes/class-newspack-blocks.php:1501
292
292
  msgid "Jetpack donations is disabled in favour of Newspack donations."
293
293
  msgstr ""
294
294
 
295
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:26
295
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:26
296
296
  #: src/shared/js/utils.js:54
297
297
  msgid "Draft"
298
298
  msgstr ""
299
299
 
300
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:26
300
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:26
301
301
  #: src/shared/js/utils.js:54
302
302
  msgid "Scheduled"
303
303
  msgstr ""
304
304
 
305
305
  #. Translators: %s is the %s is the frequency.
306
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:48
306
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:48
307
307
  #, php-format
308
308
  msgid "per %s"
309
309
  msgstr ""
310
310
 
311
- #: includes/class-newspack-blocks.php:1685
311
+ #: includes/class-newspack-blocks.php:1764
312
312
  msgid " once"
313
313
  msgstr ""
314
314
 
315
- #: includes/class-newspack-blocks.php:1688
315
+ #: includes/class-newspack-blocks.php:1767
316
316
  msgid " per "
317
317
  msgstr ""
318
318
 
@@ -2,7 +2,7 @@ msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Newspack Blocks 1.0.0-alpha.25\n"
4
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
5
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
5
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
6
6
  "PO-Revision-Date: 2024-08-30 08:46-0700\n"
7
7
  "Last-Translator: \n"
8
8
  "Language-Team: \n"
@@ -272,45 +272,45 @@ msgstr ""
272
272
  msgid "Newspack Subscribe"
273
273
  msgstr ""
274
274
 
275
- #: includes/class-newspack-blocks.php:983
275
+ #: includes/class-newspack-blocks.php:1062
276
276
  msgid "Common"
277
277
  msgstr ""
278
278
 
279
279
  #. translators: separates last two sponsor names; needs a space on either side.
280
- #: includes/class-newspack-blocks.php:1084
280
+ #: includes/class-newspack-blocks.php:1163
281
281
  msgid " and "
282
282
  msgstr " et "
283
283
 
284
284
  #. translators: separates all but the last two sponsor names; needs a space at the end.
285
- #: includes/class-newspack-blocks.php:1087
285
+ #: includes/class-newspack-blocks.php:1166
286
286
  msgid ", "
287
287
  msgstr ""
288
288
 
289
- #: includes/class-newspack-blocks.php:1422
289
+ #: includes/class-newspack-blocks.php:1501
290
290
  msgid "Jetpack donations is disabled in favour of Newspack donations."
291
291
  msgstr ""
292
292
 
293
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:26
293
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:26
294
294
  #: src/shared/js/utils.js:54
295
295
  msgid "Draft"
296
296
  msgstr ""
297
297
 
298
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:26
298
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:26
299
299
  #: src/shared/js/utils.js:54
300
300
  msgid "Scheduled"
301
301
  msgstr ""
302
302
 
303
303
  #. Translators: %s is the %s is the frequency.
304
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:48
304
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:48
305
305
  #, php-format
306
306
  msgid "per %s"
307
307
  msgstr ""
308
308
 
309
- #: includes/class-newspack-blocks.php:1685
309
+ #: includes/class-newspack-blocks.php:1764
310
310
  msgid " once"
311
311
  msgstr ""
312
312
 
313
- #: includes/class-newspack-blocks.php:1688
313
+ #: includes/class-newspack-blocks.php:1767
314
314
  msgid " per "
315
315
  msgstr ""
316
316
 
@@ -2,7 +2,7 @@ msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Newspack Blocks 1.0.0-alpha.20\n"
4
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
5
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
5
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
6
6
  "PO-Revision-Date: 2024-08-30 08:46-0700\n"
7
7
  "Last-Translator: \n"
8
8
  "Language-Team: \n"
@@ -272,45 +272,45 @@ msgstr ""
272
272
  msgid "Newspack Subscribe"
273
273
  msgstr ""
274
274
 
275
- #: includes/class-newspack-blocks.php:983
275
+ #: includes/class-newspack-blocks.php:1062
276
276
  msgid "Common"
277
277
  msgstr ""
278
278
 
279
279
  #. translators: separates last two sponsor names; needs a space on either side.
280
- #: includes/class-newspack-blocks.php:1084
280
+ #: includes/class-newspack-blocks.php:1163
281
281
  msgid " and "
282
282
  msgstr " og "
283
283
 
284
284
  #. translators: separates all but the last two sponsor names; needs a space at the end.
285
- #: includes/class-newspack-blocks.php:1087
285
+ #: includes/class-newspack-blocks.php:1166
286
286
  msgid ", "
287
287
  msgstr ""
288
288
 
289
- #: includes/class-newspack-blocks.php:1422
289
+ #: includes/class-newspack-blocks.php:1501
290
290
  msgid "Jetpack donations is disabled in favour of Newspack donations."
291
291
  msgstr ""
292
292
 
293
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:26
293
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:26
294
294
  #: src/shared/js/utils.js:54
295
295
  msgid "Draft"
296
296
  msgstr ""
297
297
 
298
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:26
298
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:26
299
299
  #: src/shared/js/utils.js:54
300
300
  msgid "Scheduled"
301
301
  msgstr ""
302
302
 
303
303
  #. Translators: %s is the %s is the frequency.
304
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:48
304
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:48
305
305
  #, php-format
306
306
  msgid "per %s"
307
307
  msgstr ""
308
308
 
309
- #: includes/class-newspack-blocks.php:1685
309
+ #: includes/class-newspack-blocks.php:1764
310
310
  msgid " once"
311
311
  msgstr ""
312
312
 
313
- #: includes/class-newspack-blocks.php:1688
313
+ #: includes/class-newspack-blocks.php:1767
314
314
  msgid " per "
315
315
  msgstr ""
316
316
 
@@ -2,7 +2,7 @@ msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Newspack Blocks 1.0.0-alpha.25\n"
4
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
5
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
5
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
6
6
  "PO-Revision-Date: 2024-08-30 08:46-0700\n"
7
7
  "Last-Translator: \n"
8
8
  "Language-Team: \n"
@@ -272,45 +272,45 @@ msgstr ""
272
272
  msgid "Newspack Subscribe"
273
273
  msgstr ""
274
274
 
275
- #: includes/class-newspack-blocks.php:983
275
+ #: includes/class-newspack-blocks.php:1062
276
276
  msgid "Common"
277
277
  msgstr ""
278
278
 
279
279
  #. translators: separates last two sponsor names; needs a space on either side.
280
- #: includes/class-newspack-blocks.php:1084
280
+ #: includes/class-newspack-blocks.php:1163
281
281
  msgid " and "
282
282
  msgstr " e "
283
283
 
284
284
  #. translators: separates all but the last two sponsor names; needs a space at the end.
285
- #: includes/class-newspack-blocks.php:1087
285
+ #: includes/class-newspack-blocks.php:1166
286
286
  msgid ", "
287
287
  msgstr ""
288
288
 
289
- #: includes/class-newspack-blocks.php:1422
289
+ #: includes/class-newspack-blocks.php:1501
290
290
  msgid "Jetpack donations is disabled in favour of Newspack donations."
291
291
  msgstr ""
292
292
 
293
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:26
293
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:26
294
294
  #: src/shared/js/utils.js:54
295
295
  msgid "Draft"
296
296
  msgstr ""
297
297
 
298
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:26
298
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:26
299
299
  #: src/shared/js/utils.js:54
300
300
  msgid "Scheduled"
301
301
  msgstr ""
302
302
 
303
303
  #. Translators: %s is the %s is the frequency.
304
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:48
304
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:48
305
305
  #, php-format
306
306
  msgid "per %s"
307
307
  msgstr ""
308
308
 
309
- #: includes/class-newspack-blocks.php:1685
309
+ #: includes/class-newspack-blocks.php:1764
310
310
  msgid " once"
311
311
  msgstr ""
312
312
 
313
- #: includes/class-newspack-blocks.php:1688
313
+ #: includes/class-newspack-blocks.php:1767
314
314
  msgid " per "
315
315
  msgstr ""
316
316
 
@@ -2,14 +2,14 @@
2
2
  # This file is distributed under the same license as the Newspack Blocks plugin.
3
3
  msgid ""
4
4
  msgstr ""
5
- "Project-Id-Version: Newspack Blocks 4.30.5\n"
5
+ "Project-Id-Version: Newspack Blocks 4.31.0\n"
6
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/newspack-blocks\n"
7
7
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
8
8
  "Language-Team: LANGUAGE <LL@li.org>\n"
9
9
  "MIME-Version: 1.0\n"
10
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "POT-Creation-Date: 2026-08-31T17:53:31+00:00\n"
12
+ "POT-Creation-Date: 2026-09-09T13:54:18+00:00\n"
13
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
14
  "X-Generator: WP-CLI 2.12.0\n"
15
15
  "X-Domain: newspack-blocks\n"
@@ -263,48 +263,48 @@ msgstr ""
263
263
  msgid "Newspack Subscribe"
264
264
  msgstr ""
265
265
 
266
- #: includes/class-newspack-blocks.php:983
266
+ #: includes/class-newspack-blocks.php:1062
267
267
  msgid "Common"
268
268
  msgstr ""
269
269
 
270
270
  #. translators: separates last two sponsor names; needs a space on either side.
271
- #: includes/class-newspack-blocks.php:1084
271
+ #: includes/class-newspack-blocks.php:1163
272
272
  msgid " and "
273
273
  msgstr ""
274
274
 
275
275
  #. translators: separates all but the last two sponsor names; needs a space at the end.
276
- #: includes/class-newspack-blocks.php:1087
276
+ #: includes/class-newspack-blocks.php:1166
277
277
  msgid ", "
278
278
  msgstr ""
279
279
 
280
- #: includes/class-newspack-blocks.php:1422
280
+ #: includes/class-newspack-blocks.php:1501
281
281
  msgid "Jetpack donations is disabled in favour of Newspack donations."
282
282
  msgstr ""
283
283
 
284
- #: includes/class-newspack-blocks.php:1455
284
+ #: includes/class-newspack-blocks.php:1534
285
285
  #: dist/editor.js:26
286
286
  #: src/shared/js/utils.js:54
287
287
  msgid "Draft"
288
288
  msgstr ""
289
289
 
290
- #: includes/class-newspack-blocks.php:1456
290
+ #: includes/class-newspack-blocks.php:1535
291
291
  #: dist/editor.js:26
292
292
  #: src/shared/js/utils.js:54
293
293
  msgid "Scheduled"
294
294
  msgstr ""
295
295
 
296
296
  #. Translators: %s is the %s is the frequency.
297
- #: includes/class-newspack-blocks.php:1654
297
+ #: includes/class-newspack-blocks.php:1733
298
298
  #: dist/editor.js:48
299
299
  #, php-format,js-format
300
300
  msgid "per %s"
301
301
  msgstr ""
302
302
 
303
- #: includes/class-newspack-blocks.php:1685
303
+ #: includes/class-newspack-blocks.php:1764
304
304
  msgid " once"
305
305
  msgstr ""
306
306
 
307
- #: includes/class-newspack-blocks.php:1688
307
+ #: includes/class-newspack-blocks.php:1767
308
308
  msgid " per "
309
309
  msgstr ""
310
310
 
@@ -7,7 +7,7 @@
7
7
  * Author URI: https://newspack.com/
8
8
  * Text Domain: newspack-blocks
9
9
  * Domain Path: /languages
10
- * Version: 4.31.0
10
+ * Version: 4.31.2
11
11
  *
12
12
  * @package Newspack_Blocks
13
13
  */
@@ -15,7 +15,7 @@
15
15
  define( 'NEWSPACK_BLOCKS__PLUGIN_FILE', __FILE__ );
16
16
  define( 'NEWSPACK_BLOCKS__BLOCKS_DIRECTORY', 'dist/' );
17
17
  define( 'NEWSPACK_BLOCKS__PLUGIN_DIR', plugin_dir_path( NEWSPACK_BLOCKS__PLUGIN_FILE ) );
18
- define( 'NEWSPACK_BLOCKS__VERSION', '4.31.0' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.31.2' );
19
19
 
20
20
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks.php';
21
21
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-api.php';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "4.31.0",
3
+ "version": "4.31.2",
4
4
  "author": "Automattic",
5
5
  "description": "=== Newspack Blocks === Contributors: (this should be a list of wordpress.org userid's) Donate link: https://example.com/ Tags: comments, spam Requires at least: 4.5 Tested up to: 5.1.1 Stable tag: 0.1.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html",
6
6
  "repository": {
@@ -9,17 +9,47 @@ import domReady from '@wordpress/dom-ready';
9
9
 
10
10
  import './view.scss';
11
11
 
12
+ /**
13
+ * Whether the frame still shows its initial about:blank, so it has loaded nothing.
14
+ * Google Docs Viewer intermittently answers HTTP 204, which leaves the frame blank
15
+ * with no load event. That is the case the retry exists for.
16
+ *
17
+ * contentDocument is null for a loaded cross-origin document, a detached frame, and
18
+ * a sandboxed frame without allow-same-origin (from the first tick, so such a frame
19
+ * would never retry; this block renders no sandbox attribute).
20
+ *
21
+ * @param {HTMLIFrameElement} iframe The iframe.
22
+ * @return {boolean} Whether the frame is still on its initial about:blank.
23
+ */
24
+ const isStillBlank = iframe => {
25
+ const doc = iframe.contentDocument;
26
+ return doc !== null && doc.URL === 'about:blank';
27
+ };
28
+
29
+ /**
30
+ * Re-navigations before giving up on a frame that never loads. A 204 resolves in a
31
+ * retry or two; without a bound, a permanently blank embed keeps requesting for as
32
+ * long as the page is open.
33
+ */
34
+ const MAX_RETRIES = 10;
35
+
12
36
  domReady( () => {
13
37
  const iframes = Array.from( document.querySelectorAll( '.wp-block-newspack-blocks-iframe iframe' ) );
14
38
  iframes.forEach( iframe => {
15
- const timerId = setInterval( function () {
16
- iframe.src = iframe.src;
39
+ // Don't wait for the load event: it may have fired before this script ran
40
+ // (delayed JS). Navigate with location.replace(), since resetting src on a
41
+ // loaded frame adds a history entry and makes readers press Back twice
42
+ // (NPPM-3180).
43
+ let attempts = 0;
44
+ const retry = setInterval( () => {
45
+ if ( ! isStillBlank( iframe ) || attempts >= MAX_RETRIES ) {
46
+ clearInterval( retry );
47
+ return;
48
+ }
49
+ attempts++;
50
+ iframe.contentWindow.location.replace( iframe.src );
17
51
  }, 2000 );
18
52
 
19
- iframe.onload = function () {
20
- clearInterval( timerId );
21
- };
22
-
23
53
  // Add a listener for dynamic resizing if the iframe supports it.
24
54
  window.addEventListener( 'message', function ( event ) {
25
55
  // Reject messages from untrusted origins.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The iframe block's retry (NPPM-3180) must leave a frame that already holds a
3
+ * document alone, and must never reset `src`, which adds a history entry.
4
+ */
5
+
6
+ const SRC = 'https://embed.example.test/widget';
7
+
8
+ /**
9
+ * Render one iframe block and stub the frame state jsdom cannot provide.
10
+ *
11
+ * @param {Object|null} contentDocument What the attached frame reports: the initial
12
+ * about:blank document while a navigation is in
13
+ * flight or was abandoned (cross-origin included),
14
+ * null once a cross-origin document has loaded.
15
+ * A detached frame reports null, as does its
16
+ * contentWindow.
17
+ * @return {Object} The iframe, its state holder, and the `replace` and `setSrc` spies.
18
+ */
19
+ function renderIframe( contentDocument ) {
20
+ document.body.innerHTML = `<figure class="wp-block-newspack-blocks-iframe"><div class="wp-block-embed__wrapper"><iframe src="${ SRC }"></iframe></div></figure>`;
21
+ const iframe = document.querySelector( 'iframe' );
22
+ const state = { contentDocument };
23
+ const replace = jest.fn();
24
+ const setSrc = jest.fn();
25
+ const contentWindow = { location: { replace } };
26
+ Object.defineProperty( iframe, 'contentDocument', { configurable: true, get: () => ( iframe.isConnected ? state.contentDocument : null ) } );
27
+ Object.defineProperty( iframe, 'contentWindow', { configurable: true, get: () => ( iframe.isConnected ? contentWindow : null ) } );
28
+ Object.defineProperty( iframe, 'src', { configurable: true, get: () => SRC, set: setSrc } );
29
+ jest.isolateModules( () => require( './view' ) );
30
+ return { iframe, state, replace, setSrc };
31
+ }
32
+
33
+ describe( 'iframe block view script', () => {
34
+ beforeEach( () => {
35
+ jest.useFakeTimers();
36
+ } );
37
+
38
+ afterEach( () => {
39
+ jest.clearAllTimers();
40
+ jest.useRealTimers();
41
+ document.body.innerHTML = '';
42
+ } );
43
+
44
+ it.each( [
45
+ // The delayed-script case: the frame loaded before this script ran.
46
+ [ 'cross-origin', null ],
47
+ [ 'same-origin', { URL: 'https://example.test/embed' } ],
48
+ ] )( 'leaves a frame that already holds a %s document alone', ( _, contentDocument ) => {
49
+ const { replace, setSrc } = renderIframe( contentDocument );
50
+ jest.advanceTimersByTime( 10000 );
51
+ expect( replace ).not.toHaveBeenCalled();
52
+ expect( setSrc ).not.toHaveBeenCalled();
53
+ expect( jest.getTimerCount() ).toBe( 0 );
54
+ } );
55
+
56
+ it( 'retries with location.replace while the frame is still blank, then stops', () => {
57
+ const { state, replace, setSrc } = renderIframe( { URL: 'about:blank' } );
58
+
59
+ jest.advanceTimersByTime( 2000 );
60
+ expect( replace ).toHaveBeenCalledTimes( 1 );
61
+ expect( replace ).toHaveBeenCalledWith( SRC );
62
+
63
+ jest.advanceTimersByTime( 2000 );
64
+ expect( replace ).toHaveBeenCalledTimes( 2 );
65
+
66
+ // The retry succeeded and a cross-origin document loaded.
67
+ state.contentDocument = null;
68
+ jest.advanceTimersByTime( 10000 );
69
+ expect( replace ).toHaveBeenCalledTimes( 2 );
70
+ expect( jest.getTimerCount() ).toBe( 0 );
71
+
72
+ // Resetting src is what adds the history entry.
73
+ expect( setSrc ).not.toHaveBeenCalled();
74
+ } );
75
+
76
+ it( 'gives up after 10 attempts on a frame that never loads', () => {
77
+ const { replace } = renderIframe( { URL: 'about:blank' } );
78
+ jest.advanceTimersByTime( 20000 );
79
+ expect( replace ).toHaveBeenCalledTimes( 10 );
80
+
81
+ jest.advanceTimersByTime( 20000 );
82
+ expect( replace ).toHaveBeenCalledTimes( 10 );
83
+ expect( jest.getTimerCount() ).toBe( 0 );
84
+ } );
85
+
86
+ it( 'checks the frame before touching contentWindow, so a removed frame does not throw', () => {
87
+ const { iframe, replace } = renderIframe( { URL: 'about:blank' } );
88
+ jest.advanceTimersByTime( 2000 );
89
+ expect( replace ).toHaveBeenCalledTimes( 1 );
90
+
91
+ // A detached frame has no contentWindow; dereferencing it would throw inside the tick.
92
+ iframe.remove();
93
+ expect( () => jest.advanceTimersByTime( 10000 ) ).not.toThrow();
94
+ expect( replace ).toHaveBeenCalledTimes( 1 );
95
+ expect( jest.getTimerCount() ).toBe( 0 );
96
+ } );
97
+ } );
@@ -3,7 +3,7 @@
3
3
  'name' => 'automattic/newspack-blocks',
4
4
  'pretty_version' => 'dev-main',
5
5
  'version' => 'dev-main',
6
- 'reference' => 'ab8a41932e2833aaadaea7e35f6cbd805c8a2d1e',
6
+ 'reference' => '6a87cb2c6dcab735a81b54e87fb11b93769baac1',
7
7
  'type' => 'wordpress-plugin',
8
8
  'install_path' => __DIR__ . '/../../',
9
9
  'aliases' => array(),
@@ -13,7 +13,7 @@
13
13
  'automattic/newspack-blocks' => array(
14
14
  'pretty_version' => 'dev-main',
15
15
  'version' => 'dev-main',
16
- 'reference' => 'ab8a41932e2833aaadaea7e35f6cbd805c8a2d1e',
16
+ 'reference' => '6a87cb2c6dcab735a81b54e87fb11b93769baac1',
17
17
  'type' => 'wordpress-plugin',
18
18
  'install_path' => __DIR__ . '/../../',
19
19
  'aliases' => array(),