@automattic/newspack-blocks 4.32.0-alpha.1 → 4.32.0-alpha.3

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,21 +1,23 @@
1
- # @automattic/newspack-blocks [4.32.0-alpha.1](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.31.0...newspack-blocks@4.32.0-alpha.1) (2026-09-03)
1
+ # @automattic/newspack-blocks [4.32.0-alpha.3](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.32.0-alpha.2...newspack-blocks@4.32.0-alpha.3) (2026-09-10)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * **blocks:** keep tag labels clear of cat-links styling ([#736](https://github.com/Automattic/newspack-workspace/issues/736)) ([4cb3fb7](https://github.com/Automattic/newspack-workspace/commit/4cb3fb76849ca05bf29d06788e0148afab204227))
7
- * **modal-checkout:** tiered donate URL trigger handles non-default frequency ([#202](https://github.com/Automattic/newspack-workspace/issues/202)) ([1bc89b9](https://github.com/Automattic/newspack-workspace/commit/1bc89b94e0db1110b8dc35536907cf35a98f62dd))
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))
8
7
 
8
+ ## @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)
9
9
 
10
- ### Features
11
10
 
12
- * **blocks:** reorder the content picked in Static mode (NPPD-946) ([#846](https://github.com/Automattic/newspack-workspace/issues/846)) ([b4b9177](https://github.com/Automattic/newspack-workspace/commit/b4b91774a4a391a9686352edda0e64ffebfce5d4))
13
- * **group-subscriptions:** per-seat pricing ([#955](https://github.com/Automattic/newspack-workspace/issues/955)) ([a13a858](https://github.com/Automattic/newspack-workspace/commit/a13a858cbf1570cc6a60ca61b5672758e9b3dc59))
11
+ ### Bug Fixes
14
12
 
13
+ * **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))
15
14
 
16
- ### Dependencies
15
+ ## @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)
16
+
17
+
18
+ ### Bug Fixes
17
19
 
18
- * **newspack-components:** upgraded to 4.8.0-alpha.1
20
+ * **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))
19
21
 
20
22
  # @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)
21
23
 
@@ -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-09-03T10:39:01+00:00\n"
5
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -309,45 +309,45 @@ msgstr ""
309
309
  msgid "Newspack Subscribe"
310
310
  msgstr ""
311
311
 
312
- #: includes/class-newspack-blocks.php:983
312
+ #: includes/class-newspack-blocks.php:1062
313
313
  msgid "Common"
314
314
  msgstr ""
315
315
 
316
316
  #. translators: separates last two sponsor names; needs a space on either side.
317
- #: includes/class-newspack-blocks.php:1084
317
+ #: includes/class-newspack-blocks.php:1163
318
318
  msgid " and "
319
319
  msgstr " und "
320
320
 
321
321
  #. translators: separates all but the last two sponsor names; needs a space at the end.
322
- #: includes/class-newspack-blocks.php:1087
322
+ #: includes/class-newspack-blocks.php:1166
323
323
  msgid ", "
324
324
  msgstr ""
325
325
 
326
- #: includes/class-newspack-blocks.php:1422
326
+ #: includes/class-newspack-blocks.php:1501
327
327
  msgid "Jetpack donations is disabled in favour of Newspack donations."
328
328
  msgstr ""
329
329
 
330
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:30
330
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:30
331
331
  #: src/shared/js/utils.js:54
332
332
  msgid "Draft"
333
333
  msgstr ""
334
334
 
335
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:30
335
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:30
336
336
  #: src/shared/js/utils.js:54
337
337
  msgid "Scheduled"
338
338
  msgstr ""
339
339
 
340
340
  #. Translators: %s is the %s is the frequency.
341
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:52
341
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:52
342
342
  #, php-format
343
343
  msgid "per %s"
344
344
  msgstr ""
345
345
 
346
- #: includes/class-newspack-blocks.php:1685
346
+ #: includes/class-newspack-blocks.php:1764
347
347
  msgid " once"
348
348
  msgstr ""
349
349
 
350
- #: includes/class-newspack-blocks.php:1688
350
+ #: includes/class-newspack-blocks.php:1767
351
351
  msgid " per "
352
352
  msgstr ""
353
353
 
@@ -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-09-03T10:39:01+00:00\n"
5
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -301,45 +301,45 @@ msgstr ""
301
301
  msgid "Newspack Subscribe"
302
302
  msgstr ""
303
303
 
304
- #: includes/class-newspack-blocks.php:983
304
+ #: includes/class-newspack-blocks.php:1062
305
305
  msgid "Common"
306
306
  msgstr ""
307
307
 
308
308
  #. translators: separates last two sponsor names; needs a space on either side.
309
- #: includes/class-newspack-blocks.php:1084
309
+ #: includes/class-newspack-blocks.php:1163
310
310
  msgid " and "
311
311
  msgstr " y "
312
312
 
313
313
  #. translators: separates all but the last two sponsor names; needs a space at the end.
314
- #: includes/class-newspack-blocks.php:1087
314
+ #: includes/class-newspack-blocks.php:1166
315
315
  msgid ", "
316
316
  msgstr ""
317
317
 
318
- #: includes/class-newspack-blocks.php:1422
318
+ #: includes/class-newspack-blocks.php:1501
319
319
  msgid "Jetpack donations is disabled in favour of Newspack donations."
320
320
  msgstr ""
321
321
 
322
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:30
322
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:30
323
323
  #: src/shared/js/utils.js:54
324
324
  msgid "Draft"
325
325
  msgstr ""
326
326
 
327
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:30
327
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:30
328
328
  #: src/shared/js/utils.js:54
329
329
  msgid "Scheduled"
330
330
  msgstr ""
331
331
 
332
332
  #. Translators: %s is the %s is the frequency.
333
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:52
333
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:52
334
334
  #, php-format
335
335
  msgid "per %s"
336
336
  msgstr ""
337
337
 
338
- #: includes/class-newspack-blocks.php:1685
338
+ #: includes/class-newspack-blocks.php:1764
339
339
  msgid " once"
340
340
  msgstr ""
341
341
 
342
- #: includes/class-newspack-blocks.php:1688
342
+ #: includes/class-newspack-blocks.php:1767
343
343
  msgid " per "
344
344
  msgstr ""
345
345
 
@@ -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-09-03T10:39:01+00:00\n"
5
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -301,45 +301,45 @@ msgstr ""
301
301
  msgid "Newspack Subscribe"
302
302
  msgstr ""
303
303
 
304
- #: includes/class-newspack-blocks.php:983
304
+ #: includes/class-newspack-blocks.php:1062
305
305
  msgid "Common"
306
306
  msgstr ""
307
307
 
308
308
  #. translators: separates last two sponsor names; needs a space on either side.
309
- #: includes/class-newspack-blocks.php:1084
309
+ #: includes/class-newspack-blocks.php:1163
310
310
  msgid " and "
311
311
  msgstr " et "
312
312
 
313
313
  #. translators: separates all but the last two sponsor names; needs a space at the end.
314
- #: includes/class-newspack-blocks.php:1087
314
+ #: includes/class-newspack-blocks.php:1166
315
315
  msgid ", "
316
316
  msgstr ""
317
317
 
318
- #: includes/class-newspack-blocks.php:1422
318
+ #: includes/class-newspack-blocks.php:1501
319
319
  msgid "Jetpack donations is disabled in favour of Newspack donations."
320
320
  msgstr ""
321
321
 
322
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:30
322
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:30
323
323
  #: src/shared/js/utils.js:54
324
324
  msgid "Draft"
325
325
  msgstr ""
326
326
 
327
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:30
327
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:30
328
328
  #: src/shared/js/utils.js:54
329
329
  msgid "Scheduled"
330
330
  msgstr ""
331
331
 
332
332
  #. Translators: %s is the %s is the frequency.
333
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:52
333
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:52
334
334
  #, php-format
335
335
  msgid "per %s"
336
336
  msgstr ""
337
337
 
338
- #: includes/class-newspack-blocks.php:1685
338
+ #: includes/class-newspack-blocks.php:1764
339
339
  msgid " once"
340
340
  msgstr ""
341
341
 
342
- #: includes/class-newspack-blocks.php:1688
342
+ #: includes/class-newspack-blocks.php:1767
343
343
  msgid " per "
344
344
  msgstr ""
345
345
 
@@ -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-09-03T10:39:01+00:00\n"
5
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -300,45 +300,45 @@ msgstr ""
300
300
  msgid "Newspack Subscribe"
301
301
  msgstr ""
302
302
 
303
- #: includes/class-newspack-blocks.php:983
303
+ #: includes/class-newspack-blocks.php:1062
304
304
  msgid "Common"
305
305
  msgstr ""
306
306
 
307
307
  #. translators: separates last two sponsor names; needs a space on either side.
308
- #: includes/class-newspack-blocks.php:1084
308
+ #: includes/class-newspack-blocks.php:1163
309
309
  msgid " and "
310
310
  msgstr " og "
311
311
 
312
312
  #. translators: separates all but the last two sponsor names; needs a space at the end.
313
- #: includes/class-newspack-blocks.php:1087
313
+ #: includes/class-newspack-blocks.php:1166
314
314
  msgid ", "
315
315
  msgstr ""
316
316
 
317
- #: includes/class-newspack-blocks.php:1422
317
+ #: includes/class-newspack-blocks.php:1501
318
318
  msgid "Jetpack donations is disabled in favour of Newspack donations."
319
319
  msgstr ""
320
320
 
321
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:30
321
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:30
322
322
  #: src/shared/js/utils.js:54
323
323
  msgid "Draft"
324
324
  msgstr ""
325
325
 
326
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:30
326
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:30
327
327
  #: src/shared/js/utils.js:54
328
328
  msgid "Scheduled"
329
329
  msgstr ""
330
330
 
331
331
  #. Translators: %s is the %s is the frequency.
332
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:52
332
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:52
333
333
  #, php-format
334
334
  msgid "per %s"
335
335
  msgstr ""
336
336
 
337
- #: includes/class-newspack-blocks.php:1685
337
+ #: includes/class-newspack-blocks.php:1764
338
338
  msgid " once"
339
339
  msgstr ""
340
340
 
341
- #: includes/class-newspack-blocks.php:1688
341
+ #: includes/class-newspack-blocks.php:1767
342
342
  msgid " per "
343
343
  msgstr ""
344
344
 
@@ -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-09-03T10:39:01+00:00\n"
5
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -299,45 +299,45 @@ msgstr ""
299
299
  msgid "Newspack Subscribe"
300
300
  msgstr ""
301
301
 
302
- #: includes/class-newspack-blocks.php:983
302
+ #: includes/class-newspack-blocks.php:1062
303
303
  msgid "Common"
304
304
  msgstr ""
305
305
 
306
306
  #. translators: separates last two sponsor names; needs a space on either side.
307
- #: includes/class-newspack-blocks.php:1084
307
+ #: includes/class-newspack-blocks.php:1163
308
308
  msgid " and "
309
309
  msgstr " e "
310
310
 
311
311
  #. translators: separates all but the last two sponsor names; needs a space at the end.
312
- #: includes/class-newspack-blocks.php:1087
312
+ #: includes/class-newspack-blocks.php:1166
313
313
  msgid ", "
314
314
  msgstr ""
315
315
 
316
- #: includes/class-newspack-blocks.php:1422
316
+ #: includes/class-newspack-blocks.php:1501
317
317
  msgid "Jetpack donations is disabled in favour of Newspack donations."
318
318
  msgstr ""
319
319
 
320
- #: includes/class-newspack-blocks.php:1455 dist/editor.js:30
320
+ #: includes/class-newspack-blocks.php:1534 dist/editor.js:30
321
321
  #: src/shared/js/utils.js:54
322
322
  msgid "Draft"
323
323
  msgstr ""
324
324
 
325
- #: includes/class-newspack-blocks.php:1456 dist/editor.js:30
325
+ #: includes/class-newspack-blocks.php:1535 dist/editor.js:30
326
326
  #: src/shared/js/utils.js:54
327
327
  msgid "Scheduled"
328
328
  msgstr ""
329
329
 
330
330
  #. Translators: %s is the %s is the frequency.
331
- #: includes/class-newspack-blocks.php:1654 dist/editor.js:52
331
+ #: includes/class-newspack-blocks.php:1733 dist/editor.js:52
332
332
  #, php-format
333
333
  msgid "per %s"
334
334
  msgstr ""
335
335
 
336
- #: includes/class-newspack-blocks.php:1685
336
+ #: includes/class-newspack-blocks.php:1764
337
337
  msgid " once"
338
338
  msgstr ""
339
339
 
340
- #: includes/class-newspack-blocks.php:1688
340
+ #: includes/class-newspack-blocks.php:1767
341
341
  msgid " per "
342
342
  msgstr ""
343
343
 
@@ -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.31.0\n"
5
+ "Project-Id-Version: Newspack Blocks 4.31.2\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-09-03T10:39:01+00:00\n"
12
+ "POT-Creation-Date: 2026-09-10T19:25:39+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"
@@ -294,48 +294,48 @@ msgstr ""
294
294
  msgid "Newspack Subscribe"
295
295
  msgstr ""
296
296
 
297
- #: includes/class-newspack-blocks.php:983
297
+ #: includes/class-newspack-blocks.php:1062
298
298
  msgid "Common"
299
299
  msgstr ""
300
300
 
301
301
  #. translators: separates last two sponsor names; needs a space on either side.
302
- #: includes/class-newspack-blocks.php:1084
302
+ #: includes/class-newspack-blocks.php:1163
303
303
  msgid " and "
304
304
  msgstr ""
305
305
 
306
306
  #. translators: separates all but the last two sponsor names; needs a space at the end.
307
- #: includes/class-newspack-blocks.php:1087
307
+ #: includes/class-newspack-blocks.php:1166
308
308
  msgid ", "
309
309
  msgstr ""
310
310
 
311
- #: includes/class-newspack-blocks.php:1422
311
+ #: includes/class-newspack-blocks.php:1501
312
312
  msgid "Jetpack donations is disabled in favour of Newspack donations."
313
313
  msgstr ""
314
314
 
315
- #: includes/class-newspack-blocks.php:1455
315
+ #: includes/class-newspack-blocks.php:1534
316
316
  #: dist/editor.js:30
317
317
  #: src/shared/js/utils.js:54
318
318
  msgid "Draft"
319
319
  msgstr ""
320
320
 
321
- #: includes/class-newspack-blocks.php:1456
321
+ #: includes/class-newspack-blocks.php:1535
322
322
  #: dist/editor.js:30
323
323
  #: src/shared/js/utils.js:54
324
324
  msgid "Scheduled"
325
325
  msgstr ""
326
326
 
327
327
  #. Translators: %s is the %s is the frequency.
328
- #: includes/class-newspack-blocks.php:1654
328
+ #: includes/class-newspack-blocks.php:1733
329
329
  #: dist/editor.js:52
330
330
  #, php-format,js-format
331
331
  msgid "per %s"
332
332
  msgstr ""
333
333
 
334
- #: includes/class-newspack-blocks.php:1685
334
+ #: includes/class-newspack-blocks.php:1764
335
335
  msgid " once"
336
336
  msgstr ""
337
337
 
338
- #: includes/class-newspack-blocks.php:1688
338
+ #: includes/class-newspack-blocks.php:1767
339
339
  msgid " per "
340
340
  msgstr ""
341
341
 
@@ -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.32.0-alpha.1
10
+ * Version: 4.32.0-alpha.3
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.32.0-alpha.1' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.32.0-alpha.3' );
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.32.0-alpha.1",
3
+ "version": "4.32.0-alpha.3",
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' => 'f1539c5feb6d64f232d84e168076ebe2b2b3132c',
6
+ 'reference' => 'bfbd263f07c45fa202296d88920a2cdd5575a4e7',
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' => 'f1539c5feb6d64f232d84e168076ebe2b2b3132c',
16
+ 'reference' => 'bfbd263f07c45fa202296d88920a2cdd5575a4e7',
17
17
  'type' => 'wordpress-plugin',
18
18
  'install_path' => __DIR__ . '/../../',
19
19
  'aliases' => array(),