@automattic/newspack-blocks 4.24.1 → 4.25.0

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/author-profile/view-rtl.css +1 -1
  3. package/dist/author-profile/view.asset.php +1 -1
  4. package/dist/author-profile/view.css +1 -1
  5. package/dist/blocks/author-list/block.json +2 -1
  6. package/dist/blocks/author-profile/block.json +18 -1
  7. package/dist/blocks/carousel/block.json +2 -1
  8. package/dist/blocks/donate/block.json +2 -1
  9. package/dist/blocks/homepage-articles/block.json +2 -1
  10. package/dist/blocks/iframe/block.json +2 -1
  11. package/dist/editor-rtl.css +2 -2
  12. package/dist/editor.asset.php +1 -1
  13. package/dist/editor.css +2 -2
  14. package/dist/editor.js +23 -19
  15. package/dist/placeholder_blocks.asset.php +1 -1
  16. package/dist/placeholder_blocks.js +1 -1
  17. package/includes/class-modal-checkout.php +43 -11
  18. package/includes/class-newspack-blocks-caching.php +13 -1
  19. package/includes/class-newspack-blocks.php +14 -7
  20. package/languages/newspack-blocks-de_DE-2d52b39fdbc5d6c94b3514803f3720b8.json +1 -1
  21. package/languages/newspack-blocks-de_DE-34e5c64f90b1444f3fc735376442eada.json +1 -1
  22. package/languages/newspack-blocks-de_DE-4fdea541976076f02d56139fb35e5b42.json +1 -1
  23. package/languages/newspack-blocks-de_DE-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  24. package/languages/newspack-blocks-de_DE-78456b164809d080adecb4d2b3895802.json +1 -1
  25. package/languages/newspack-blocks-de_DE-9ef9b2c60c897ad79f92951e6e9949a1.json +1 -1
  26. package/languages/newspack-blocks-de_DE-eccbc51a43c04f59165364eda71e0be7.json +1 -1
  27. package/languages/newspack-blocks-de_DE-fbe7f8c598cf05d4603ba49fec909ded.json +1 -1
  28. package/languages/newspack-blocks-de_DE.po +594 -493
  29. package/languages/newspack-blocks-es_ES-2d52b39fdbc5d6c94b3514803f3720b8.json +1 -1
  30. package/languages/newspack-blocks-es_ES-34e5c64f90b1444f3fc735376442eada.json +1 -1
  31. package/languages/newspack-blocks-es_ES-4fdea541976076f02d56139fb35e5b42.json +1 -1
  32. package/languages/newspack-blocks-es_ES-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  33. package/languages/newspack-blocks-es_ES-78456b164809d080adecb4d2b3895802.json +1 -1
  34. package/languages/newspack-blocks-es_ES-9ef9b2c60c897ad79f92951e6e9949a1.json +1 -1
  35. package/languages/newspack-blocks-es_ES-eccbc51a43c04f59165364eda71e0be7.json +1 -1
  36. package/languages/newspack-blocks-es_ES-fbe7f8c598cf05d4603ba49fec909ded.json +1 -1
  37. package/languages/newspack-blocks-es_ES.po +594 -493
  38. package/languages/newspack-blocks-fr_BE-2d52b39fdbc5d6c94b3514803f3720b8.json +1 -1
  39. package/languages/newspack-blocks-fr_BE-34e5c64f90b1444f3fc735376442eada.json +1 -1
  40. package/languages/newspack-blocks-fr_BE-4fdea541976076f02d56139fb35e5b42.json +1 -1
  41. package/languages/newspack-blocks-fr_BE-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  42. package/languages/newspack-blocks-fr_BE-78456b164809d080adecb4d2b3895802.json +1 -1
  43. package/languages/newspack-blocks-fr_BE-9ef9b2c60c897ad79f92951e6e9949a1.json +1 -1
  44. package/languages/newspack-blocks-fr_BE-eccbc51a43c04f59165364eda71e0be7.json +1 -1
  45. package/languages/newspack-blocks-fr_BE-fbe7f8c598cf05d4603ba49fec909ded.json +1 -1
  46. package/languages/newspack-blocks-fr_BE.po +594 -493
  47. package/languages/newspack-blocks-nb_NO-2d52b39fdbc5d6c94b3514803f3720b8.json +1 -1
  48. package/languages/newspack-blocks-nb_NO-34e5c64f90b1444f3fc735376442eada.json +1 -1
  49. package/languages/newspack-blocks-nb_NO-4fdea541976076f02d56139fb35e5b42.json +1 -1
  50. package/languages/newspack-blocks-nb_NO-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  51. package/languages/newspack-blocks-nb_NO-78456b164809d080adecb4d2b3895802.json +1 -1
  52. package/languages/newspack-blocks-nb_NO-9ef9b2c60c897ad79f92951e6e9949a1.json +1 -1
  53. package/languages/newspack-blocks-nb_NO-eccbc51a43c04f59165364eda71e0be7.json +1 -1
  54. package/languages/newspack-blocks-nb_NO-fbe7f8c598cf05d4603ba49fec909ded.json +1 -1
  55. package/languages/newspack-blocks-nb_NO.po +594 -493
  56. package/languages/newspack-blocks-pt_PT-2d52b39fdbc5d6c94b3514803f3720b8.json +1 -1
  57. package/languages/newspack-blocks-pt_PT-34e5c64f90b1444f3fc735376442eada.json +1 -1
  58. package/languages/newspack-blocks-pt_PT-4fdea541976076f02d56139fb35e5b42.json +1 -1
  59. package/languages/newspack-blocks-pt_PT-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  60. package/languages/newspack-blocks-pt_PT-78456b164809d080adecb4d2b3895802.json +1 -1
  61. package/languages/newspack-blocks-pt_PT-9ef9b2c60c897ad79f92951e6e9949a1.json +1 -1
  62. package/languages/newspack-blocks-pt_PT-eccbc51a43c04f59165364eda71e0be7.json +1 -1
  63. package/languages/newspack-blocks-pt_PT-fbe7f8c598cf05d4603ba49fec909ded.json +1 -1
  64. package/languages/newspack-blocks-pt_PT.po +594 -493
  65. package/languages/newspack-blocks.pot +806 -671
  66. package/newspack-blocks.php +5 -5
  67. package/package.json +1 -1
  68. package/src/blocks/author-list/block.json +3 -2
  69. package/src/blocks/author-list/edit.js +15 -8
  70. package/src/blocks/author-list/index.js +4 -3
  71. package/src/blocks/author-list/view.php +1 -0
  72. package/src/blocks/author-profile/block.json +16 -2
  73. package/src/blocks/author-profile/class-wp-rest-newspack-authors-controller.php +180 -9
  74. package/src/blocks/author-profile/context.js +26 -0
  75. package/src/blocks/author-profile/edit.js +847 -84
  76. package/src/blocks/author-profile/editor.scss +18 -14
  77. package/src/blocks/author-profile/index.js +14 -9
  78. package/src/blocks/author-profile/single-author.js +6 -2
  79. package/src/blocks/author-profile/view.php +386 -15
  80. package/src/blocks/author-profile/view.scss +48 -19
  81. package/src/blocks/carousel/block.json +2 -1
  82. package/src/blocks/carousel/edit.js +120 -117
  83. package/src/blocks/carousel/index.js +2 -1
  84. package/src/blocks/carousel/view.php +1 -0
  85. package/src/blocks/donate/block.json +3 -2
  86. package/src/blocks/donate/edit/FrequencyBasedLayout.tsx +2 -3
  87. package/src/blocks/donate/edit/index.tsx +56 -39
  88. package/src/blocks/donate/index.js +2 -1
  89. package/src/blocks/donate/view.php +1 -0
  90. package/src/blocks/homepage-articles/block.json +2 -1
  91. package/src/blocks/homepage-articles/edit.tsx +42 -37
  92. package/src/blocks/homepage-articles/index.js +2 -1
  93. package/src/blocks/homepage-articles/view.php +13 -1
  94. package/src/blocks/iframe/block.json +3 -2
  95. package/src/blocks/iframe/edit.js +36 -34
  96. package/src/blocks/iframe/index.js +2 -1
  97. package/src/blocks/iframe/view.php +1 -0
  98. package/src/blocks/video-playlist/edit.js +22 -15
  99. package/src/blocks/video-playlist/index.js +1 -0
  100. package/src/blocks/video-playlist/view.php +1 -0
  101. package/src/setup/placeholder-blocks.js +1 -0
  102. package/vendor/composer/installed.php +2 -2
@@ -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.24.1
10
+ * Version: 4.25.0
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.24.1' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.25.0' );
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';
@@ -81,9 +81,9 @@ function newspack_iframe_block_register_rest_routes() { // phpcs:ignore WordPres
81
81
  add_action( 'rest_api_init', 'newspack_iframe_block_register_rest_routes' );
82
82
 
83
83
  Newspack_Blocks::manage_view_scripts();
84
- add_action( 'enqueue_block_editor_assets', array( 'Newspack_Blocks', 'enqueue_block_editor_assets' ) );
85
- add_action( 'enqueue_block_editor_assets', array( 'Newspack_Blocks', 'enqueue_placeholder_blocks_assets' ), 9999 );
86
- add_action( 'wp_enqueue_scripts', array( 'Newspack_Blocks', 'enqueue_block_styles_assets' ) );
84
+ add_action( 'enqueue_block_assets', array( 'Newspack_Blocks', 'enqueue_block_assets' ) );
85
+ add_action( 'enqueue_block_assets', array( 'Newspack_Blocks', 'enqueue_placeholder_blocks_assets' ), 9999 );
86
+ add_action( 'enqueue_block_assets', array( 'Newspack_Blocks', 'enqueue_block_styles_assets' ) );
87
87
 
88
88
  /**
89
89
  * Load language files
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "4.24.1",
3
+ "version": "4.25.0",
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": {
@@ -1,7 +1,8 @@
1
1
  {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "apiVersion": 3,
2
4
  "name": "author-list",
3
5
  "category": "newspack",
4
- "apiVersion": 3,
5
6
  "attributes": {
6
7
  "className": {
7
8
  "type": "string",
@@ -80,4 +81,4 @@
80
81
  "default": false
81
82
  }
82
83
  }
83
- }
84
+ }
@@ -2,7 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import apiFetch from '@wordpress/api-fetch';
5
- import { BlockControls, InspectorControls } from '@wordpress/block-editor';
5
+ import { BlockControls, InspectorControls, useBlockProps } from '@wordpress/block-editor';
6
6
  import {
7
7
  BaseControl,
8
8
  CheckboxControl,
@@ -21,6 +21,8 @@ import {
21
21
  __experimentalToggleGroupControl as ToggleGroupControl,
22
22
  // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
23
23
  __experimentalToggleGroupControlOption as ToggleGroupControlOption,
24
+ // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
25
+ __experimentalVStack as VStack,
24
26
  } from '@wordpress/components';
25
27
  import { Fragment, useEffect, useState } from '@wordpress/element';
26
28
  import { decodeEntities } from '@wordpress/html-entities';
@@ -46,8 +48,9 @@ const AuthorList = ( { attributes, clientId, setAttributes } ) => {
46
48
  const [ error, setError ] = useState( null );
47
49
  const [ isLoading, setIsLoading ] = useState( false );
48
50
  const [ maxItemsToSuggest, setMaxItemsToSuggest ] = useState( 0 );
49
- const canUseCAP = Boolean( window.newspack_blocks_data?.can_use_cap );
50
- const editableRoles = window.newspack_blocks_data?.editable_roles;
51
+ const canUseCAP = Boolean( window?.newspack_blocks_data?.can_use_cap );
52
+ const editableRoles = window?.newspack_blocks_data?.editable_roles || [];
53
+
51
54
  const separators = [];
52
55
  const {
53
56
  authorRoles,
@@ -71,6 +74,10 @@ const AuthorList = ( { attributes, clientId, setAttributes } ) => {
71
74
  getAuthors();
72
75
  }, [ authorRoles, authorType, avatarHideDefault, exclude, excludeEmpty ] );
73
76
 
77
+ const blockProps = useBlockProps( {
78
+ className: classnames( attributes.className, 'wp-block-newspack-blocks-author-list' ),
79
+ } );
80
+
74
81
  const getAuthors = async () => {
75
82
  setError( null );
76
83
  setIsLoading( true );
@@ -361,7 +368,7 @@ const AuthorList = ( { attributes, clientId, setAttributes } ) => {
361
368
  />
362
369
  </BlockControls>
363
370
  ) }
364
- <div className={ classnames( attributes.className, 'wp-block-newspack-blocks-author-list' ) }>
371
+ <div { ...blockProps }>
365
372
  { ! isLoading && ! error && authors && Array.isArray( authors ) && (
366
373
  <>
367
374
  { isColumns && showSeparators && separatorSections ? (
@@ -434,10 +441,10 @@ const AuthorList = ( { attributes, clientId, setAttributes } ) => {
434
441
  </Notice>
435
442
  ) }
436
443
  { isLoading && (
437
- <div className="is-loading">
438
- { __( 'Fetching authors…', 'newspack-blocks' ) }
439
- <Spinner />
440
- </div>
444
+ <VStack alignment="center" style={ { width: '100%' } }>
445
+ <Spinner style={ { margin: '0' } } />
446
+ <span style={ { fontWeight: '500' } }>{ __( 'Fetching authors…', 'newspack-blocks' ) }</span>
447
+ </VStack>
441
448
  ) }
442
449
  </Placeholder>
443
450
  ) }
@@ -20,7 +20,7 @@ import edit from './edit';
20
20
  import './editor.scss';
21
21
  import './view.scss';
22
22
  import metadata from './block.json';
23
- const { name, attributes, category } = metadata;
23
+ const { name, attributes, apiVersion, category } = metadata;
24
24
 
25
25
  // Name must be exported separately.
26
26
  export { name };
@@ -37,19 +37,20 @@ authorCustomFields.forEach( field => {
37
37
  } );
38
38
 
39
39
  export const settings = {
40
+ apiVersion,
40
41
  title,
41
42
  icon: {
42
43
  src: listView,
43
44
  foreground: colors[ 'primary-400' ],
44
45
  },
46
+ attributes,
47
+ category,
45
48
  keywords: [ __( 'author', 'newspack-blocks' ), __( 'profile', 'newspack-blocks' ) ],
46
49
  description: __( 'Display a list of author profile cards.', 'newspack-blocks' ),
47
50
  styles: [
48
51
  { name: 'default', label: _x( 'Default', 'block style', 'newspack-blocks' ), isDefault: true },
49
52
  { name: 'center', label: _x( 'Centered', 'block style', 'newspack-blocks' ) },
50
53
  ],
51
- attributes,
52
- category,
53
54
  supports: {
54
55
  html: false,
55
56
  default: '',
@@ -27,6 +27,7 @@ function newspack_blocks_register_author_list() {
27
27
  register_block_type(
28
28
  'newspack-blocks/' . $block_json['name'],
29
29
  [
30
+ 'api_version' => $block_json['apiVersion'],
30
31
  'attributes' => $block_json['attributes'],
31
32
  'render_callback' => 'newspack_blocks_render_block_author_list',
32
33
  ]
@@ -1,7 +1,9 @@
1
1
  {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "apiVersion": 3,
2
4
  "name": "author-profile",
3
5
  "category": "newspack",
4
- "apiVersion": 3,
6
+ "usesContext": [ "postId", "postType" ],
5
7
  "attributes": {
6
8
  "className": {
7
9
  "type": "string",
@@ -11,6 +13,18 @@
11
13
  "type": "number",
12
14
  "default": 0
13
15
  },
16
+ "isContextual": {
17
+ "type": "boolean",
18
+ "default": false
19
+ },
20
+ "layoutVersion": {
21
+ "type": "number",
22
+ "default": 1
23
+ },
24
+ "showEmptyBio": {
25
+ "type": "boolean",
26
+ "default": true
27
+ },
14
28
  "isGuestAuthor": {
15
29
  "type": "boolean",
16
30
  "default": true
@@ -56,4 +70,4 @@
56
70
  "default": false
57
71
  }
58
72
  }
59
- }
73
+ }
@@ -58,6 +58,9 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
58
58
  'fields' => [
59
59
  'sanitize_callback' => 'sanitize_text_field',
60
60
  ],
61
+ 'post_id' => [
62
+ 'sanitize_callback' => 'absint',
63
+ ],
61
64
  ],
62
65
  'permission_callback' => function() {
63
66
  return current_user_can( 'edit_posts' );
@@ -79,9 +82,15 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
79
82
  $search = ! empty( $request->get_param( 'search' ) ) ? $request->get_param( 'search' ) : null; // Fetch authors by search string.
80
83
  $offset = ! empty( $request->get_param( 'offset' ) ) ? $request->get_param( 'offset' ) : 0; // Offset results (for pagination).
81
84
  $per_page = ! empty( $request->get_param( 'perPage' ) ) ? $request->get_param( 'perPage' ) : 10; // Number of results to return per page. This is applied to each query, so the actual number of results returned may be up to 2x this number.
82
- $avatar_hide_default = ! empty( $request->get_param( 'avatarHideDefault' ) ) ? true : false; // Hide the default avatar if the user has no custom avatar.
85
+ $avatar_hide_default = ! empty( $request->get_param( 'avatar_hide_default' ) ) ? true : false; // Hide the default avatar if the user has no custom avatar.
83
86
  $fields = ! empty( $request->get_param( 'fields' ) ) ? explode( ',', $request->get_param( 'fields' ) ) : [ 'id' ]; // Fields to get. Will return at least id.
84
87
  $include = ! empty( $request->get_param( 'include' ) ) ? explode( ',', $request->get_param( 'include' ) ) : null; // Fetch authors by multiple IDs.
88
+ $post_id = ! empty( $request->get_param( 'post_id' ) ) ? $request->get_param( 'post_id' ) : 0; // Fetch authors for a specific post (contextual mode).
89
+
90
+ // If post_id is provided, get authors for that specific post.
91
+ if ( $post_id ) {
92
+ return $this->get_post_authors( $post_id, $fields, $avatar_hide_default );
93
+ }
85
94
 
86
95
  // Total number of users and guest authors.
87
96
  $guest_author_total = 0;
@@ -300,8 +309,8 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
300
309
  $value = [
301
310
  'url' => 'tel:' . $value,
302
311
  ];
303
- if ( class_exists( 'Newspack_SVG_Icons' ) ) {
304
- $value['svg'] = Newspack_SVG_Icons::get_social_link_svg( $value['url'], 24 );
312
+ if ( class_exists( '\Newspack\Social_Icons' ) ) {
313
+ $value['svg'] = \Newspack\Social_Icons::get_svg( 'phone' );
305
314
  }
306
315
  }
307
316
  $guest_author_data[ $custom_field['name'] ] = $value;
@@ -349,8 +358,8 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
349
358
  $value = [
350
359
  'url' => 'tel:' . $value,
351
360
  ];
352
- if ( class_exists( 'Newspack_SVG_Icons' ) ) {
353
- $value['svg'] = Newspack_SVG_Icons::get_social_link_svg( $value['url'], 24 );
361
+ if ( class_exists( '\Newspack\Social_Icons' ) ) {
362
+ $value['svg'] = \Newspack\Social_Icons::get_svg( 'phone' );
354
363
  }
355
364
  }
356
365
  $user_data[ $custom_field['name'] ] = $value;
@@ -394,8 +403,8 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
394
403
  if ( $email_address ) {
395
404
  $email_data = [ 'url' => 'mailto:' . $email_address ];
396
405
 
397
- if ( class_exists( 'Newspack_SVG_Icons' ) ) {
398
- $email_data['svg'] = Newspack_SVG_Icons::get_social_link_svg( 'mailto:' . $email_address, 24 );
406
+ if ( class_exists( '\Newspack\Social_Icons' ) ) {
407
+ $email_data['svg'] = \Newspack\Social_Icons::get_svg( 'email' );
399
408
  }
400
409
  }
401
410
 
@@ -434,8 +443,8 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
434
443
  $url = 'twitter' === $profile ? esc_url( 'https://x.com/' . $handle ) : esc_url( $handle );
435
444
  $acc[ $profile ] = [ 'url' => $url ];
436
445
 
437
- if ( class_exists( 'Newspack_SVG_Icons' ) ) {
438
- $acc[ $profile ]['svg'] = $is_website ? Newspack_SVG_Icons::get_svg( 'ui', 'link', 24 ) : Newspack_SVG_Icons::get_social_link_svg( $url, 24 );
446
+ if ( class_exists( '\Newspack\Social_Icons' ) && ! $is_website ) {
447
+ $acc[ $profile ]['svg'] = \Newspack\Social_Icons::get_svg( $profile );
439
448
  }
440
449
  }
441
450
 
@@ -444,4 +453,166 @@ class WP_REST_Newspack_Authors_Controller extends WP_REST_Controller {
444
453
  []
445
454
  );
446
455
  }
456
+
457
+ /**
458
+ * Get authors for a specific post (contextual mode).
459
+ *
460
+ * Uses Co-Authors Plus if available, otherwise falls back to the default post author.
461
+ *
462
+ * @param int $post_id Post ID.
463
+ * @param array $fields Fields to include in response.
464
+ * @param bool $avatar_hide_default Whether to hide default avatars.
465
+ * @return WP_REST_Response
466
+ */
467
+ protected function get_post_authors( $post_id, $fields, $avatar_hide_default ) {
468
+ $post = get_post( $post_id );
469
+ if ( ! $post ) {
470
+ return rest_ensure_response( [] );
471
+ }
472
+
473
+ $authors = [];
474
+
475
+ // Try Co-Authors Plus first. When CAP is active, always return its result
476
+ // and never fall back to post_author, which could show the wrong person.
477
+ if ( function_exists( 'get_coauthors' ) ) {
478
+ $coauthors = get_coauthors( $post_id );
479
+ foreach ( $coauthors as $coauthor ) {
480
+ $author_data = $this->format_coauthor( $coauthor, $fields, $avatar_hide_default );
481
+ if ( $author_data ) {
482
+ $authors[] = $author_data;
483
+ }
484
+ }
485
+
486
+ $response = new WP_REST_Response( $authors );
487
+ $response->header( 'x-wp-total', count( $authors ) );
488
+ return rest_ensure_response( $response );
489
+ }
490
+
491
+ // Fallback to default post author.
492
+ $author_id = $post->post_author;
493
+ if ( $author_id ) {
494
+ $user = get_user_by( 'id', $author_id );
495
+ if ( $user ) {
496
+ $user_data = [
497
+ 'id' => intval( $user->data->ID ),
498
+ 'registered' => $user->data->user_registered,
499
+ 'is_guest' => false,
500
+ 'slug' => $user->data->user_login,
501
+ ];
502
+
503
+ if ( in_array( 'avatar', $fields, true ) ) {
504
+ $avatar = get_avatar( $user->data->ID, 256 );
505
+ if ( $avatar && ( false === strpos( $avatar, 'avatar-default' ) || ! $avatar_hide_default ) ) {
506
+ $user_data['avatar'] = $avatar;
507
+ }
508
+ }
509
+
510
+ $user_data = self::fill_user_data( $user_data, $user, $fields );
511
+ $authors[] = $user_data;
512
+ }
513
+ }
514
+
515
+ $response = new WP_REST_Response( $authors );
516
+ $response->header( 'x-wp-total', count( $authors ) );
517
+ return rest_ensure_response( $response );
518
+ }
519
+
520
+ /**
521
+ * Format a co-author object for the REST response.
522
+ *
523
+ * Co-Authors Plus returns stdClass objects from get_coauthors().
524
+ * These can be either guest authors or linked WP users.
525
+ *
526
+ * @param object $coauthor Co-author object from get_coauthors().
527
+ * @param array $fields Fields to include in response.
528
+ * @param bool $avatar_hide_default Whether to hide default avatars.
529
+ * @return array|null Formatted author data or null if invalid.
530
+ */
531
+ protected function format_coauthor( $coauthor, $fields, $avatar_hide_default ) {
532
+ if ( ! is_object( $coauthor ) || ! isset( $coauthor->ID ) ) {
533
+ return null;
534
+ }
535
+
536
+ // Check if this is a guest author.
537
+ $is_guest = isset( $coauthor->type ) && 'guest-author' === $coauthor->type;
538
+
539
+ if ( $is_guest && class_exists( 'CoAuthors_Guest_Authors' ) ) {
540
+ $guest_author = ( new CoAuthors_Guest_Authors() )->get_guest_author_by( 'id', $coauthor->ID );
541
+
542
+ if ( ! $guest_author ) {
543
+ return null;
544
+ }
545
+
546
+ $author_data = [
547
+ 'id' => intval( $coauthor->ID ),
548
+ 'registered' => isset( $coauthor->post_date ) ? $coauthor->post_date : '',
549
+ 'is_guest' => true,
550
+ 'slug' => isset( $coauthor->user_nicename ) ? $coauthor->user_nicename : '',
551
+ ];
552
+
553
+ if ( in_array( 'avatar', $fields, true ) && function_exists( 'coauthors_get_avatar' ) ) {
554
+ $avatar = coauthors_get_avatar( $guest_author, 256 );
555
+ if ( $avatar && ( false === strpos( $avatar, 'avatar-default' ) || ! $avatar_hide_default ) ) {
556
+ $author_data['avatar'] = $avatar;
557
+ }
558
+ }
559
+
560
+ return self::fill_guest_author_data( $author_data, $guest_author, $fields );
561
+ }
562
+
563
+ // It's a linked WP user.
564
+ $user = get_user_by( 'id', $coauthor->ID );
565
+ if ( ! $user ) {
566
+ // Try to get by login if ID doesn't work.
567
+ if ( isset( $coauthor->user_login ) ) {
568
+ $user = get_user_by( 'login', $coauthor->user_login );
569
+ }
570
+ }
571
+
572
+ if ( ! $user ) {
573
+ return null;
574
+ }
575
+
576
+ // Check if this user has a linked guest author for additional data.
577
+ $linked_guest_author = self::get_linked_guest_author( $user->user_login );
578
+
579
+ if ( $linked_guest_author && class_exists( 'CoAuthors_Guest_Authors' ) ) {
580
+ $guest_author = ( new CoAuthors_Guest_Authors() )->get_guest_author_by( 'id', $linked_guest_author->ID );
581
+
582
+ if ( $guest_author ) {
583
+ $author_data = [
584
+ 'id' => intval( $linked_guest_author->ID ),
585
+ 'registered' => $linked_guest_author->post_date,
586
+ 'is_guest' => true,
587
+ 'slug' => $linked_guest_author->post_name,
588
+ ];
589
+
590
+ if ( in_array( 'avatar', $fields, true ) && function_exists( 'coauthors_get_avatar' ) ) {
591
+ $avatar = coauthors_get_avatar( $guest_author, 256 );
592
+ if ( $avatar && ( false === strpos( $avatar, 'avatar-default' ) || ! $avatar_hide_default ) ) {
593
+ $author_data['avatar'] = $avatar;
594
+ }
595
+ }
596
+
597
+ return self::fill_guest_author_data( $author_data, $guest_author, $fields );
598
+ }
599
+ }
600
+
601
+ // Return as regular WP user.
602
+ $user_data = [
603
+ 'id' => intval( $user->data->ID ),
604
+ 'registered' => $user->data->user_registered,
605
+ 'is_guest' => false,
606
+ 'slug' => $user->data->user_login,
607
+ ];
608
+
609
+ if ( in_array( 'avatar', $fields, true ) ) {
610
+ $avatar = get_avatar( $user->data->ID, 256 );
611
+ if ( $avatar && ( false === strpos( $avatar, 'avatar-default' ) || ! $avatar_hide_default ) ) {
612
+ $user_data['avatar'] = $avatar;
613
+ }
614
+ }
615
+
616
+ return self::fill_user_data( $user_data, $user, $fields );
617
+ }
447
618
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { createContext } from '@wordpress/element';
5
+
6
+ /**
7
+ * React context for author data in the editor.
8
+ *
9
+ * This context is used to pass author data to inner blocks in nested mode.
10
+ * It avoids serializing author data into post content by using React context
11
+ * instead of block attributes/context.
12
+ *
13
+ * For PHP render, inner blocks use block context via `usesContext: ['newspack-blocks/author']`
14
+ * which is injected at runtime via `new WP_Block()`.
15
+ */
16
+ export const AuthorContext = createContext( null );
17
+
18
+ /**
19
+ * Expose AuthorContext globally so that blocks from other packages (like newspack-plugin's
20
+ * avatar block) can consume it when nested inside the Author Profile block.
21
+ *
22
+ * This creates a loose coupling without hard package dependencies.
23
+ */
24
+ if ( typeof window !== 'undefined' ) {
25
+ window.NewspackAuthorContext = AuthorContext;
26
+ }