Full Site Editing - Version 0.6.1

Version Description

  • Updates priority of filter so classnames are added properly to the template blocks.
Download this release

Release Info

Developer gwwar
Plugin Icon wp plugin Full Site Editing
Version 0.6.1
Comparing to
See all releases

Code changes from version 0.4 to 0.6.1

Files changed (44) hide show
  1. full-site-editing-plugin.php +32 -13
  2. full-site-editing/blocks/navigation-menu/index.php +37 -23
  3. full-site-editing/blocks/post-content/edit.js +4 -62
  4. full-site-editing/blocks/post-content/index.js +24 -3
  5. full-site-editing/blocks/post-content/index.php +3 -10
  6. full-site-editing/blocks/post-content/save.js +1 -4
  7. full-site-editing/blocks/post-content/style.scss +31 -0
  8. full-site-editing/blocks/site-description/edit.js +26 -4
  9. full-site-editing/blocks/site-description/index.php +3 -1
  10. full-site-editing/blocks/site-title/edit.js +26 -4
  11. full-site-editing/blocks/site-title/index.php +3 -1
  12. full-site-editing/blocks/template/edit.js +64 -22
  13. full-site-editing/blocks/template/index.js +30 -5
  14. full-site-editing/blocks/template/index.php +5 -3
  15. full-site-editing/blocks/template/site-logo.js +18 -0
  16. full-site-editing/blocks/template/style.scss +60 -8
  17. full-site-editing/class-full-site-editing.php +248 -350
  18. full-site-editing/dist/full-site-editing.css +1 -1
  19. full-site-editing/dist/full-site-editing.deps.json +1 -1
  20. full-site-editing/dist/full-site-editing.js +2 -2
  21. full-site-editing/dist/full-site-editing.rtl.css +1 -1
  22. full-site-editing/editor/image-block-keywords/index.js +23 -0
  23. full-site-editing/editor/index.js +2 -0
  24. full-site-editing/editor/style.scss +61 -0
  25. full-site-editing/editor/template-validity-override/index.js +0 -2
  26. full-site-editing/index.js +2 -3
  27. full-site-editing/plugins/close-button-override/index.js +22 -11
  28. full-site-editing/plugins/close-button-override/style.scss +13 -0
  29. full-site-editing/plugins/editor-template-classes/index.js +51 -0
  30. full-site-editing/plugins/template-update-notice/index.js +20 -0
  31. full-site-editing/serialize-block-fallback.php +70 -0
  32. full-site-editing/templates/class-rest-templates-controller.php +49 -0
  33. full-site-editing/templates/class-wp-template-inserter.php +388 -0
  34. full-site-editing/templates/class-wp-template.php +182 -0
  35. posts-list-block/class-posts-list-block.php +7 -5
  36. posts-list-block/dist/posts-list-block.deps.json +1 -1
  37. posts-list-block/dist/posts-list-block.js +1 -1
  38. posts-list-block/templates/no-posts.php +1 -1
  39. posts-list-block/templates/posts-list.php +1 -1
  40. posts-list-block/utils.php +33 -33
  41. readme.txt +11 -1
  42. starter-page-templates/class-starter-page-templates.php +5 -3
  43. starter-page-templates/dist/starter-page-templates.deps.json +1 -1
  44. starter-page-templates/dist/starter-page-templates.js +2 -2
full-site-editing-plugin.php CHANGED
@@ -2,15 +2,17 @@
2
  /**
3
  * Plugin Name: Full Site Editing
4
  * Description: Enhances your page creation workflow within the Block Editor.
5
- * Version: 0.4
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/wordpress-plugins/
8
  * License: GPLv2 or later
9
  * Text Domain: full-site-editing
10
  *
11
- * @package full-site-editing
12
  */
13
 
 
 
14
  /**
15
  * Plugin version.
16
  *
@@ -18,12 +20,12 @@
18
  *
19
  * @var string
20
  */
21
- define( 'A8C_FSE_VERSION', '0.4' );
22
 
23
  /**
24
  * Load Full Site Editing.
25
  */
26
- function a8c_load_full_site_editing() {
27
  /**
28
  * Can be used to disable Full Site Editing functionality.
29
  *
@@ -35,25 +37,25 @@ function a8c_load_full_site_editing() {
35
  return;
36
  }
37
 
38
- require_once __DIR__ . '/lib/feature-flags/class-a8c-full-site-editing-feature-flags.php';
39
  require_once __DIR__ . '/full-site-editing/blocks/navigation-menu/index.php';
40
  require_once __DIR__ . '/full-site-editing/blocks/post-content/index.php';
41
  require_once __DIR__ . '/full-site-editing/blocks/site-description/index.php';
42
  require_once __DIR__ . '/full-site-editing/blocks/site-title/index.php';
43
  require_once __DIR__ . '/full-site-editing/blocks/template/index.php';
44
- require_once __DIR__ . '/full-site-editing/blocks/site-logo/index.php';
45
- require_once __DIR__ . '/full-site-editing/class-a8c-rest-templates-controller.php';
46
  require_once __DIR__ . '/full-site-editing/class-full-site-editing.php';
47
- require_once __DIR__ . '/full-site-editing/utils/class-a8c-wp-template.php';
 
 
 
48
 
49
  Full_Site_Editing::get_instance();
50
  }
51
- add_action( 'plugins_loaded', 'a8c_load_full_site_editing' );
52
 
53
  /**
54
  * Load Posts List Block.
55
  */
56
- function a8c_load_posts_list_block() {
57
  if ( class_exists( 'Posts_List_Block' ) ) {
58
  return;
59
  }
@@ -74,12 +76,12 @@ function a8c_load_posts_list_block() {
74
 
75
  Posts_List_Block::get_instance();
76
  }
77
- add_action( 'plugins_loaded', 'a8c_load_posts_list_block' );
78
 
79
  /**
80
  * Load Starter_Page_Templates.
81
  */
82
- function a8c_load_starter_page_templates() {
83
  /**
84
  * Can be used to disable the Starter Page Templates.
85
  *
@@ -95,4 +97,21 @@ function a8c_load_starter_page_templates() {
95
 
96
  Starter_Page_Templates::get_instance();
97
  }
98
- add_action( 'plugins_loaded', 'a8c_load_starter_page_templates' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Plugin Name: Full Site Editing
4
  * Description: Enhances your page creation workflow within the Block Editor.
5
+ * Version: 0.6.1
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/wordpress-plugins/
8
  * License: GPLv2 or later
9
  * Text Domain: full-site-editing
10
  *
11
+ * @package A8C\FSE
12
  */
13
 
14
+ namespace A8C\FSE;
15
+
16
  /**
17
  * Plugin version.
18
  *
20
  *
21
  * @var string
22
  */
23
+ define( 'PLUGIN_VERSION', '0.6.1' );
24
 
25
  /**
26
  * Load Full Site Editing.
27
  */
28
+ function load_full_site_editing() {
29
  /**
30
  * Can be used to disable Full Site Editing functionality.
31
  *
37
  return;
38
  }
39
 
 
40
  require_once __DIR__ . '/full-site-editing/blocks/navigation-menu/index.php';
41
  require_once __DIR__ . '/full-site-editing/blocks/post-content/index.php';
42
  require_once __DIR__ . '/full-site-editing/blocks/site-description/index.php';
43
  require_once __DIR__ . '/full-site-editing/blocks/site-title/index.php';
44
  require_once __DIR__ . '/full-site-editing/blocks/template/index.php';
 
 
45
  require_once __DIR__ . '/full-site-editing/class-full-site-editing.php';
46
+ require_once __DIR__ . '/full-site-editing/templates/class-rest-templates-controller.php';
47
+ require_once __DIR__ . '/full-site-editing/templates/class-wp-template.php';
48
+ require_once __DIR__ . '/full-site-editing/templates/class-wp-template-inserter.php';
49
+ require_once __DIR__ . '/full-site-editing/serialize-block-fallback.php';
50
 
51
  Full_Site_Editing::get_instance();
52
  }
53
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\load_full_site_editing' );
54
 
55
  /**
56
  * Load Posts List Block.
57
  */
58
+ function load_posts_list_block() {
59
  if ( class_exists( 'Posts_List_Block' ) ) {
60
  return;
61
  }
76
 
77
  Posts_List_Block::get_instance();
78
  }
79
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\load_posts_list_block' );
80
 
81
  /**
82
  * Load Starter_Page_Templates.
83
  */
84
+ function load_starter_page_templates() {
85
  /**
86
  * Can be used to disable the Starter Page Templates.
87
  *
97
 
98
  Starter_Page_Templates::get_instance();
99
  }
100
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\load_starter_page_templates' );
101
+
102
+ /**
103
+ * Inserts default full site editing data for current theme during plugin activation.
104
+ *
105
+ * We usually perform this on theme activation hook, but this is needed to handle
106
+ * the cases in which FSE supported theme was activated prior to the plugin. This will
107
+ * populate the default header and footer for current theme, and create About and Contact
108
+ * pages provided that they don't already exist.
109
+ */
110
+ function populate_wp_template_data() {
111
+ require_once __DIR__ . '/full-site-editing/class-full-site-editing.php';
112
+ require_once __DIR__ . '/full-site-editing/templates/class-wp-template-inserter.php';
113
+
114
+ $fse = Full_Site_Editing::get_instance();
115
+ $fse->insert_default_data();
116
+ }
117
+ register_activation_hook( __FILE__, __NAMESPACE__ . '\populate_wp_template_data' );
full-site-editing/blocks/navigation-menu/index.php CHANGED
@@ -1,26 +1,38 @@
1
  <?php
2
  /**
3
- * Render navigation menus.
4
  *
5
- * @package full-site-editing
6
  */
7
- function a8c_fse_render_navigation_menu_block( $attributes ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ob_start();
9
- // phpcs:disable WordPress.WP.I18n.NonSingularStringLiteralText
10
  ?>
11
  <nav class="main-navigation wp-block-a8c-navigation-menu">
12
  <div class="menu-nav-container">
13
- <?php
14
- echo wp_nav_menu([
15
- 'theme_location' => 'menu-1',
16
- 'menu_class' => 'main-menu',
17
- 'fallback_cb' => 'a8c_fse_get_fallback_navigation_menu'
18
- ]);
19
- ?>
20
  </div>
21
  </nav>
22
  <!-- #site-navigation -->
23
  <?php
 
24
  return ob_get_clean();
25
  }
26
 
@@ -28,25 +40,27 @@ function a8c_fse_render_navigation_menu_block( $attributes ) {
28
  * Render a list of all site pages as a fallback
29
  * for when a menu does not exist.
30
  *
31
- * @package full-site-editing
32
  */
33
- function a8c_fse_get_fallback_navigation_menu() {
34
- $menu = wp_page_menu([
35
- 'echo' => 0,
36
- 'sort_column' => 'post_date',
37
- 'container' => 'ul',
38
- 'menu_class' => 'main-menu default-menu',
39
- 'before' => false,
40
- 'after' => false
41
- ]);
 
 
42
 
43
  /**
44
  * Filter the fallback page menu to use the same
45
  * CSS class structure as a regularly built menu
46
  * so we don't have to duplicate CSS selectors everywhere.
47
  */
48
- $original_classes = [ 'children', 'page_item_has_sub-menu' ];
49
  $replacement_classes = [ 'sub-menu', 'menu-item-has-children' ];
50
 
51
  return str_replace( $original_classes, $replacement_classes, $menu );
52
- }
1
  <?php
2
  /**
3
+ * Render navigation menu block file.
4
  *
5
+ * @package A8C\FSE
6
  */
7
+
8
+ namespace A8C\FSE;
9
+
10
+ /**
11
+ * Render the navigation menu.
12
+ *
13
+ * @return string
14
+ */
15
+ function render_navigation_menu_block() {
16
+ $menu = wp_nav_menu(
17
+ [
18
+ 'echo' => false,
19
+ 'menu_class' => 'main-menu',
20
+ 'fallback_cb' => 'a8c_fse_get_fallback_navigation_menu',
21
+ 'theme_location' => 'menu-1',
22
+ ]
23
+ );
24
+
25
  ob_start();
26
+ // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
27
  ?>
28
  <nav class="main-navigation wp-block-a8c-navigation-menu">
29
  <div class="menu-nav-container">
30
+ <?php echo $menu ? $menu : get_fallback_navigation_menu(); ?>
 
 
 
 
 
 
31
  </div>
32
  </nav>
33
  <!-- #site-navigation -->
34
  <?php
35
+ // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
36
  return ob_get_clean();
37
  }
38
 
40
  * Render a list of all site pages as a fallback
41
  * for when a menu does not exist.
42
  *
43
+ * @return string
44
  */
45
+ function get_fallback_navigation_menu() {
46
+ $menu = wp_page_menu(
47
+ [
48
+ 'after' => false,
49
+ 'before' => false,
50
+ 'container' => 'ul',
51
+ 'echo' => false,
52
+ 'menu_class' => 'main-menu default-menu',
53
+ 'sort_column' => 'post_date',
54
+ ]
55
+ );
56
 
57
  /**
58
  * Filter the fallback page menu to use the same
59
  * CSS class structure as a regularly built menu
60
  * so we don't have to duplicate CSS selectors everywhere.
61
  */
62
+ $original_classes = [ 'children', 'page_item_has_sub-menu' ];
63
  $replacement_classes = [ 'sub-menu', 'menu-item-has-children' ];
64
 
65
  return str_replace( $original_classes, $replacement_classes, $menu );
66
+ }
full-site-editing/blocks/post-content/edit.js CHANGED
@@ -1,26 +1,17 @@
1
  /* eslint-disable wpcalypso/jsx-classname-namespace */
2
- /* global fullSiteEditing */
3
 
4
  /**
5
  * External dependencies
6
  */
7
  import classNames from 'classnames';
8
- import { get } from 'lodash';
9
 
10
  /**
11
  * WordPress dependencies
12
  */
13
- import { IconButton, Placeholder, Toolbar } from '@wordpress/components';
14
  import { compose, withState } from '@wordpress/compose';
15
  import { withSelect } from '@wordpress/data';
16
- import { BlockControls, InnerBlocks, PostTitle } from '@wordpress/editor';
17
- import { Component, Fragment, RawHTML } from '@wordpress/element';
18
- import { __, sprintf } from '@wordpress/i18n';
19
-
20
- /**
21
- * Internal dependencies
22
- */
23
- import PostAutocomplete from '../../components/post-autocomplete';
24
 
25
  class PostContentEdit extends Component {
26
  toggleEditing() {
@@ -37,67 +28,18 @@ class PostContentEdit extends Component {
37
  }
38
 
39
  render() {
40
- const { attributes, isEditing, selectedPost } = this.props;
41
  const { align } = attributes;
42
 
43
- const isTemplatePostType = 'wp_template' === fullSiteEditing.editorPostType;
44
- const showToggleButton = isTemplatePostType && ( ! isEditing || !! selectedPost );
45
- const showPlaceholder = isTemplatePostType && ( isEditing || ! selectedPost );
46
- const showPreview = isTemplatePostType && ! isEditing && !! selectedPost;
47
- const showInnerBlocks = ! isTemplatePostType;
48
-
49
  return (
50
  <Fragment>
51
- { showToggleButton && (
52
- <BlockControls>
53
- <Toolbar>
54
- <IconButton
55
- className={ classNames( 'components-icon-button components-toolbar__control', {
56
- 'is-active': isEditing,
57
- } ) }
58
- label={ __( 'Change Preview' ) }
59
- onClick={ this.toggleEditing }
60
- icon="edit"
61
- />
62
- </Toolbar>
63
- </BlockControls>
64
- ) }
65
  <div
66
  className={ classNames( 'post-content-block', {
67
  [ `align${ align }` ]: align,
68
  } ) }
69
  >
70
  <PostTitle />
71
- { showInnerBlocks && <InnerBlocks templateLock={ false } /> }
72
- { showPlaceholder && (
73
- <Placeholder
74
- icon="layout"
75
- label={ __( 'Content Slot' ) }
76
- instructions={ __( 'Placeholder for a post or a page.' ) }
77
- >
78
- <div className="post-content-block__selector">
79
- <div>{ __( 'Select something to preview:' ) }</div>
80
- <PostAutocomplete
81
- initialValue={ get( selectedPost, [ 'title', 'rendered' ] ) }
82
- onSelectPost={ this.onSelectPost }
83
- postType={ [ 'page', 'post' ] }
84
- />
85
- { !! selectedPost && (
86
- <a href={ `?post=${ selectedPost.id }&action=edit` }>
87
- { sprintf(
88
- __( 'Edit "%s"' ),
89
- get( selectedPost, [ 'title', 'rendered' ], '' )
90
- ) }
91
- </a>
92
- ) }
93
- </div>
94
- </Placeholder>
95
- ) }
96
- { showPreview && (
97
- <RawHTML className="post-content-block__preview">
98
- { get( selectedPost, [ 'content', 'rendered' ] ) }
99
- </RawHTML>
100
- ) }
101
  </div>
102
  </Fragment>
103
  );
1
  /* eslint-disable wpcalypso/jsx-classname-namespace */
 
2
 
3
  /**
4
  * External dependencies
5
  */
6
  import classNames from 'classnames';
 
7
 
8
  /**
9
  * WordPress dependencies
10
  */
 
11
  import { compose, withState } from '@wordpress/compose';
12
  import { withSelect } from '@wordpress/data';
13
+ import { InnerBlocks, PostTitle } from '@wordpress/editor';
14
+ import { Component, Fragment } from '@wordpress/element';
 
 
 
 
 
 
15
 
16
  class PostContentEdit extends Component {
17
  toggleEditing() {
28
  }
29
 
30
  render() {
31
+ const { attributes } = this.props;
32
  const { align } = attributes;
33
 
 
 
 
 
 
 
34
  return (
35
  <Fragment>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  <div
37
  className={ classNames( 'post-content-block', {
38
  [ `align${ align }` ]: align,
39
  } ) }
40
  >
41
  <PostTitle />
42
+ <InnerBlocks templateLock={ false } />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  </div>
44
  </Fragment>
45
  );
full-site-editing/blocks/post-content/index.js CHANGED
@@ -1,8 +1,10 @@
1
  /**
2
  * External dependencies
3
  */
 
4
  import { registerBlockType } from '@wordpress/blocks';
5
  import { __ } from '@wordpress/i18n';
 
6
 
7
  /**
8
  * Internal dependencies
@@ -12,12 +14,13 @@ import save from './save';
12
  import './style.scss';
13
 
14
  registerBlockType( 'a8c/post-content', {
15
- title: __( 'Content Slot' ),
16
- description: __( 'Placeholder for a post or a page.' ),
17
  icon: 'layout',
18
  category: 'layout',
19
  supports: {
20
- anchor: true,
 
21
  html: false,
22
  multiple: false,
23
  reusable: false,
@@ -25,3 +28,21 @@ registerBlockType( 'a8c/post-content', {
25
  edit,
26
  save,
27
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
  * External dependencies
3
  */
4
+ import { createHigherOrderComponent } from '@wordpress/compose';
5
  import { registerBlockType } from '@wordpress/blocks';
6
  import { __ } from '@wordpress/i18n';
7
+ import { addFilter } from '@wordpress/hooks';
8
 
9
  /**
10
  * Internal dependencies
14
  import './style.scss';
15
 
16
  registerBlockType( 'a8c/post-content', {
17
+ title: __( 'Content' ),
18
+ description: __( 'The page content.' ),
19
  icon: 'layout',
20
  category: 'layout',
21
  supports: {
22
+ anchor: false,
23
+ customClassName: false,
24
  html: false,
25
  multiple: false,
26
  reusable: false,
28
  edit,
29
  save,
30
  } );
31
+
32
+ const addContentSlotClassname = createHigherOrderComponent( BlockListBlock => {
33
+ return props => {
34
+ if ( props.name !== 'a8c/post-content' ) {
35
+ return <BlockListBlock { ...props } />;
36
+ }
37
+
38
+ return <BlockListBlock { ...props } className={ 'post-content__block' } />;
39
+ };
40
+ }, 'addContentSlotClassname' );
41
+
42
+ // Must be 9 or this breaks on Simple Sites
43
+ addFilter(
44
+ 'editor.BlockListBlock',
45
+ 'full-site-editing/blocks/post-content',
46
+ addContentSlotClassname,
47
+ 9
48
+ );
full-site-editing/blocks/post-content/index.php CHANGED
@@ -2,9 +2,11 @@
2
  /**
3
  * Render post content block file.
4
  *
5
- * @package full-site-editing
6
  */
7
 
 
 
8
  /**
9
  * Renders post content.
10
  *
@@ -18,15 +20,6 @@ function render_post_content_block( $attributes, $content ) {
18
  return $content;
19
  }
20
 
21
- $post_id = get_the_ID();
22
- $post_type = get_post_type();
23
- $template_id = get_post_meta( $post_id, '_wp_template_id', true );
24
-
25
- // Early return to avoid the infinite loop of a template rendering itself.
26
- if ( 'wp_template' === $post_type || $template_id === $post_id ) {
27
- return $content;
28
- }
29
-
30
  $align = isset( $attributes['align'] ) ? ' align' . $attributes['align'] : '';
31
 
32
  ob_start();
2
  /**
3
  * Render post content block file.
4
  *
5
+ * @package A8C\FSE
6
  */
7
 
8
+ namespace A8C\FSE;
9
+
10
  /**
11
  * Renders post content.
12
  *
20
  return $content;
21
  }
22
 
 
 
 
 
 
 
 
 
 
23
  $align = isset( $attributes['align'] ) ? ' align' . $attributes['align'] : '';
24
 
25
  ob_start();
full-site-editing/blocks/post-content/save.js CHANGED
@@ -1,9 +1,6 @@
1
- /* global fullSiteEditing */
2
-
3
  /**
4
  * External dependencies
5
  */
6
  import { InnerBlocks } from '@wordpress/editor';
7
 
8
- const isTemplatePostType = 'wp_template' === fullSiteEditing.editorPostType;
9
- export default ( isTemplatePostType ? () => null : () => <InnerBlocks.Content /> );
 
 
1
  /**
2
  * External dependencies
3
  */
4
  import { InnerBlocks } from '@wordpress/editor';
5
 
6
+ export default () => <InnerBlocks.Content />;
 
full-site-editing/blocks/post-content/style.scss CHANGED
@@ -33,3 +33,34 @@
33
  display: block;
34
  }
35
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  display: block;
34
  }
35
  }
36
+
37
+ /* Hide the content slot block UI */
38
+ .block-editor-block-list__layout {
39
+ .post-content__block {
40
+ &.is-selected {
41
+ .block-editor-block-contextual-toolbar {
42
+ display: none;
43
+ }
44
+ }
45
+
46
+ &.block-editor-block-list__block {
47
+ // Need to get super specific to override the core css selectors:
48
+ &,
49
+ &.has-child-selected,
50
+ &.is-navigate-mode,
51
+ &.is-hovered {
52
+ > .block-editor-block-list__block-edit {
53
+ &::before {
54
+ transition: none;
55
+ border: none;
56
+ outline: none;
57
+ box-shadow: none;
58
+ }
59
+ > .block-editor-block-list__breadcrumb {
60
+ display: none;
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
full-site-editing/blocks/site-description/edit.js CHANGED
@@ -6,6 +6,7 @@ import { compose } from '@wordpress/compose';
6
  import { withSelect, withDispatch } from '@wordpress/data';
7
  import { Fragment } from '@wordpress/element';
8
  import { __ } from '@wordpress/i18n';
 
9
 
10
  /**
11
  * Internal dependencies
@@ -18,6 +19,8 @@ function SiteDescriptionEdit( {
18
  shouldUpdateSiteOption,
19
  isSelected,
20
  setAttributes,
 
 
21
  } ) {
22
  const inititalDescription = __( 'Site description loading…' );
23
 
@@ -32,12 +35,23 @@ function SiteDescriptionEdit( {
32
 
33
  const { option } = siteOptions;
34
 
 
 
 
 
 
 
 
 
 
 
35
  return (
36
  <Fragment>
37
  <PlainText
38
  className={ className }
39
  value={ option }
40
  onChange={ value => handleChange( value ) }
 
41
  placeholder={ __( 'Site Description' ) }
42
  aria-label={ __( 'Site Description' ) }
43
  />
@@ -46,17 +60,25 @@ function SiteDescriptionEdit( {
46
  }
47
 
48
  export default compose( [
49
- withDispatch( dispatch => ( {
50
- createErrorNotice: dispatch( 'core/notices' ).createErrorNotice,
51
- } ) ),
52
- withSelect( select => {
53
  const { isSavingPost, isPublishingPost, isAutosavingPost, isCurrentPostPublished } = select(
54
  'core/editor'
55
  );
 
 
 
56
  return {
 
 
 
57
  shouldUpdateSiteOption:
58
  ( ( isSavingPost() && isCurrentPostPublished() ) || isPublishingPost() ) &&
59
  ! isAutosavingPost(),
60
  };
61
  } ),
 
 
 
 
 
62
  ] )( SiteDescriptionEdit );
6
  import { withSelect, withDispatch } from '@wordpress/data';
7
  import { Fragment } from '@wordpress/element';
8
  import { __ } from '@wordpress/i18n';
9
+ import { ENTER } from '@wordpress/keycodes';
10
 
11
  /**
12
  * Internal dependencies
19
  shouldUpdateSiteOption,
20
  isSelected,
21
  setAttributes,
22
+ isLocked,
23
+ insertDefaultBlock,
24
  } ) {
25
  const inititalDescription = __( 'Site description loading…' );
26
 
35
 
36
  const { option } = siteOptions;
37
 
38
+ const onKeyDown = event => {
39
+ if ( event.keyCode !== ENTER ) {
40
+ return;
41
+ }
42
+ event.preventDefault();
43
+ if ( ! isLocked ) {
44
+ insertDefaultBlock();
45
+ }
46
+ };
47
+
48
  return (
49
  <Fragment>
50
  <PlainText
51
  className={ className }
52
  value={ option }
53
  onChange={ value => handleChange( value ) }
54
+ onKeyDown={ onKeyDown }
55
  placeholder={ __( 'Site Description' ) }
56
  aria-label={ __( 'Site Description' ) }
57
  />
60
  }
61
 
62
  export default compose( [
63
+ withSelect( ( select, { clientId } ) => {
 
 
 
64
  const { isSavingPost, isPublishingPost, isAutosavingPost, isCurrentPostPublished } = select(
65
  'core/editor'
66
  );
67
+ const { getBlockIndex, getBlockRootClientId, getTemplateLock } = select( 'core/block-editor' );
68
+ const rootClientId = getBlockRootClientId( clientId );
69
+
70
  return {
71
+ blockIndex: getBlockIndex( clientId, rootClientId ),
72
+ isLocked: !! getTemplateLock( rootClientId ),
73
+ rootClientId,
74
  shouldUpdateSiteOption:
75
  ( ( isSavingPost() && isCurrentPostPublished() ) || isPublishingPost() ) &&
76
  ! isAutosavingPost(),
77
  };
78
  } ),
79
+ withDispatch( ( dispatch, { blockIndex, rootClientId } ) => ( {
80
+ createErrorNotice: dispatch( 'core/notices' ).createErrorNotice,
81
+ insertDefaultBlock: () =>
82
+ dispatch( 'core/block-editor' ).insertDefaultBlock( {}, rootClientId, blockIndex + 1 ),
83
+ } ) ),
84
  ] )( SiteDescriptionEdit );
full-site-editing/blocks/site-description/index.php CHANGED
@@ -2,9 +2,11 @@
2
  /**
3
  * Render site description file.
4
  *
5
- * @package full-site-editing
6
  */
7
 
 
 
8
  /**
9
  * Renders the site description (tagline) block.
10
  *
2
  /**
3
  * Render site description file.
4
  *
5
+ * @package A8C\FSE
6
  */
7
 
8
+ namespace A8C\FSE;
9
+
10
  /**
11
  * Renders the site description (tagline) block.
12
  *
full-site-editing/blocks/site-title/edit.js CHANGED
@@ -11,6 +11,7 @@ import { PlainText } from '@wordpress/editor';
11
  import { withSelect, withDispatch } from '@wordpress/data';
12
  import { compose } from '@wordpress/compose';
13
  import { Fragment } from '@wordpress/element';
 
14
 
15
  /**
16
  * Internal dependencies
@@ -23,6 +24,8 @@ function SiteTitleEdit( {
23
  shouldUpdateSiteOption,
24
  isSelected,
25
  setAttributes,
 
 
26
  } ) {
27
  const inititalTitle = __( 'Site title loading…' );
28
  const { siteOptions, handleChange } = useSiteOptions(
@@ -36,12 +39,23 @@ function SiteTitleEdit( {
36
 
37
  const { option } = siteOptions;
38
 
 
 
 
 
 
 
 
 
 
 
39
  return (
40
  <Fragment>
41
  <PlainText
42
  className={ classNames( 'site-title', className ) }
43
  value={ option }
44
  onChange={ value => handleChange( value ) }
 
45
  placeholder={ __( 'Site Title' ) }
46
  aria-label={ __( 'Site Title' ) }
47
  />
@@ -50,17 +64,25 @@ function SiteTitleEdit( {
50
  }
51
 
52
  export default compose( [
53
- withDispatch( dispatch => ( {
54
- createErrorNotice: dispatch( 'core/notices' ).createErrorNotice,
55
- } ) ),
56
- withSelect( select => {
57
  const { isSavingPost, isPublishingPost, isAutosavingPost, isCurrentPostPublished } = select(
58
  'core/editor'
59
  );
 
 
 
60
  return {
 
 
 
61
  shouldUpdateSiteOption:
62
  ( ( isSavingPost() && isCurrentPostPublished() ) || isPublishingPost() ) &&
63
  ! isAutosavingPost(),
64
  };
65
  } ),
 
 
 
 
 
66
  ] )( SiteTitleEdit );
11
  import { withSelect, withDispatch } from '@wordpress/data';
12
  import { compose } from '@wordpress/compose';
13
  import { Fragment } from '@wordpress/element';
14
+ import { ENTER } from '@wordpress/keycodes';
15
 
16
  /**
17
  * Internal dependencies
24
  shouldUpdateSiteOption,
25
  isSelected,
26
  setAttributes,
27
+ isLocked,
28
+ insertDefaultBlock,
29
  } ) {
30
  const inititalTitle = __( 'Site title loading…' );
31
  const { siteOptions, handleChange } = useSiteOptions(
39
 
40
  const { option } = siteOptions;
41
 
42
+ const onKeyDown = event => {
43
+ if ( event.keyCode !== ENTER ) {
44
+ return;
45
+ }
46
+ event.preventDefault();
47
+ if ( ! isLocked ) {
48
+ insertDefaultBlock();
49
+ }
50
+ };
51
+
52
  return (
53
  <Fragment>
54
  <PlainText
55
  className={ classNames( 'site-title', className ) }
56
  value={ option }
57
  onChange={ value => handleChange( value ) }
58
+ onKeyDown={ onKeyDown }
59
  placeholder={ __( 'Site Title' ) }
60
  aria-label={ __( 'Site Title' ) }
61
  />
64
  }
65
 
66
  export default compose( [
67
+ withSelect( ( select, { clientId } ) => {
 
 
 
68
  const { isSavingPost, isPublishingPost, isAutosavingPost, isCurrentPostPublished } = select(
69
  'core/editor'
70
  );
71
+ const { getBlockIndex, getBlockRootClientId, getTemplateLock } = select( 'core/block-editor' );
72
+ const rootClientId = getBlockRootClientId( clientId );
73
+
74
  return {
75
+ blockIndex: getBlockIndex( clientId, rootClientId ),
76
+ isLocked: !! getTemplateLock( rootClientId ),
77
+ rootClientId,
78
  shouldUpdateSiteOption:
79
  ( ( isSavingPost() && isCurrentPostPublished() ) || isPublishingPost() ) &&
80
  ! isAutosavingPost(),
81
  };
82
  } ),
83
+ withDispatch( ( dispatch, { blockIndex, rootClientId } ) => ( {
84
+ createErrorNotice: dispatch( 'core/notices' ).createErrorNotice,
85
+ insertDefaultBlock: () =>
86
+ dispatch( 'core/block-editor' ).insertDefaultBlock( {}, rootClientId, blockIndex + 1 ),
87
+ } ) ),
88
  ] )( SiteTitleEdit );
full-site-editing/blocks/site-title/index.php CHANGED
@@ -2,9 +2,11 @@
2
  /**
3
  * Render site title block.
4
  *
5
- * @package full-site-editing
6
  */
7
 
 
 
8
  /**
9
  * Renders the site title and allows for editing in the full site editor.
10
  *
2
  /**
3
  * Render site title block.
4
  *
5
+ * @package A8C\FSE
6
  */
7
 
8
+ namespace A8C\FSE;
9
+
10
  /**
11
  * Renders the site title and allows for editing in the full site editor.
12
  *
full-site-editing/blocks/template/edit.js CHANGED
@@ -14,7 +14,7 @@ import { BlockEdit } from '@wordpress/editor';
14
  import { Button, Placeholder, Spinner, Disabled } from '@wordpress/components';
15
  import { compose, withState } from '@wordpress/compose';
16
  import { withDispatch, withSelect } from '@wordpress/data';
17
- import { Fragment, useEffect } from '@wordpress/element';
18
  import { __, sprintf } from '@wordpress/i18n';
19
  import { addQueryArgs } from '@wordpress/url';
20
 
@@ -24,33 +24,37 @@ import { addQueryArgs } from '@wordpress/url';
24
  import './style.scss';
25
 
26
  const TemplateEdit = compose(
27
- withState( { templateClientId: null } ),
28
  withSelect( ( select, { attributes, templateClientId } ) => {
29
  const { getEntityRecord } = select( 'core' );
30
- const { getCurrentPostId } = select( 'core/editor' );
31
  const { getBlock } = select( 'core/block-editor' );
32
-
33
  const { templateId } = attributes;
34
  const currentPostId = getCurrentPostId();
35
- const template = templateId && getEntityRecord( 'postType', 'wp_template_part', templateId );
36
- const editTemplatePartUrl = addQueryArgs( fullSiteEditing.editTemplatePartBaseUrl, {
37
  post: templateId,
38
  fse_parent_post: currentPostId,
39
  } );
40
 
41
  return {
42
  currentPostId,
43
- editTemplatePartUrl,
44
  template,
45
  templateBlock: getBlock( templateClientId ),
46
  templateTitle: get( template, [ 'title', 'rendered' ], '' ),
 
 
47
  };
48
  } ),
49
  withDispatch( ( dispatch, ownProps ) => {
50
  const { receiveBlocks } = dispatch( 'core/block-editor' );
 
 
51
  const { template, templateClientId, setState } = ownProps;
52
-
53
  return {
 
54
  receiveTemplateBlocks: () => {
55
  if ( ! template || templateClientId ) {
56
  return;
@@ -65,16 +69,26 @@ const TemplateEdit = compose(
65
  receiveBlocks( [ templateBlock ] );
66
  setState( { templateClientId: templateBlock.clientId } );
67
  },
 
 
68
  };
69
  } )
70
  )(
71
  ( {
72
  attributes,
73
- editTemplatePartUrl,
74
  receiveTemplateBlocks,
75
  template,
76
  templateBlock,
77
  templateTitle,
 
 
 
 
 
 
 
 
78
  } ) => {
79
  if ( ! template ) {
80
  return (
@@ -83,16 +97,47 @@ const TemplateEdit = compose(
83
  </Placeholder>
84
  );
85
  }
86
-
 
87
  useEffect( () => {
 
 
 
 
 
 
 
88
  receiveTemplateBlocks();
89
  } );
90
 
91
- const { align } = attributes;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  return (
94
  <div
95
- className={ classNames( 'template-block', {
96
  [ `align${ align }` ]: align,
97
  } ) }
98
  >
@@ -108,16 +153,13 @@ const TemplateEdit = compose(
108
  setAttributes={ noop }
109
  />
110
  </Disabled>
111
- <Placeholder
112
- className="template-block__overlay"
113
- instructions={ __(
114
- 'This block is part of your site template and may appear on multiple pages.'
115
- ) }
116
- >
117
- <Button href={ editTemplatePartUrl } isDefault>
118
- { sprintf( __( 'Edit %s' ), templateTitle ) }
119
- </Button>
120
- </Placeholder>
121
  </Fragment>
122
  ) }
123
  </div>
14
  import { Button, Placeholder, Spinner, Disabled } from '@wordpress/components';
15
  import { compose, withState } from '@wordpress/compose';
16
  import { withDispatch, withSelect } from '@wordpress/data';
17
+ import { Fragment, useEffect, useState, createRef } from '@wordpress/element';
18
  import { __, sprintf } from '@wordpress/i18n';
19
  import { addQueryArgs } from '@wordpress/url';
20
 
24
  import './style.scss';
25
 
26
  const TemplateEdit = compose(
27
+ withState( { templateClientId: null, shouldCloseSidebarOnSelect: true } ),
28
  withSelect( ( select, { attributes, templateClientId } ) => {
29
  const { getEntityRecord } = select( 'core' );
30
+ const { getCurrentPostId, isEditedPostDirty } = select( 'core/editor' );
31
  const { getBlock } = select( 'core/block-editor' );
32
+ const { isEditorSidebarOpened } = select( 'core/edit-post' );
33
  const { templateId } = attributes;
34
  const currentPostId = getCurrentPostId();
35
+ const template = templateId && getEntityRecord( 'postType', 'wp_template', templateId );
36
+ const editTemplateUrl = addQueryArgs( fullSiteEditing.editTemplateBaseUrl, {
37
  post: templateId,
38
  fse_parent_post: currentPostId,
39
  } );
40
 
41
  return {
42
  currentPostId,
43
+ editTemplateUrl,
44
  template,
45
  templateBlock: getBlock( templateClientId ),
46
  templateTitle: get( template, [ 'title', 'rendered' ], '' ),
47
+ isDirty: isEditedPostDirty(),
48
+ isEditorSidebarOpened: !! isEditorSidebarOpened(),
49
  };
50
  } ),
51
  withDispatch( ( dispatch, ownProps ) => {
52
  const { receiveBlocks } = dispatch( 'core/block-editor' );
53
+ const { closeGeneralSidebar } = dispatch( 'core/edit-post' );
54
+ const { clearSelectedBlock } = dispatch( 'core/editor' );
55
  const { template, templateClientId, setState } = ownProps;
 
56
  return {
57
+ savePost: dispatch( 'core/editor' ).savePost,
58
  receiveTemplateBlocks: () => {
59
  if ( ! template || templateClientId ) {
60
  return;
69
  receiveBlocks( [ templateBlock ] );
70
  setState( { templateClientId: templateBlock.clientId } );
71
  },
72
+ closeGeneralSidebar,
73
+ clearSelectedBlock,
74
  };
75
  } )
76
  )(
77
  ( {
78
  attributes,
79
+ editTemplateUrl,
80
  receiveTemplateBlocks,
81
  template,
82
  templateBlock,
83
  templateTitle,
84
+ isDirty,
85
+ savePost,
86
+ isSelected,
87
+ isEditorSidebarOpened,
88
+ closeGeneralSidebar,
89
+ clearSelectedBlock,
90
+ shouldCloseSidebarOnSelect,
91
+ setState,
92
  } ) => {
93
  if ( ! template ) {
94
  return (
97
  </Placeholder>
98
  );
99
  }
100
+ const navButton = createRef();
101
+ const [ navigateToTemplate, setNavigateToTemplate ] = useState( false );
102
  useEffect( () => {
103
+ if ( navigateToTemplate && ! isDirty ) {
104
+ // Since we cancelled the click event to save the post
105
+ // we trigger it again here. We do this instead of setting
106
+ // window.location.href because in WordPress.com, the navigation
107
+ // scheme is different and not available to us here.
108
+ navButton.current.click();
109
+ }
110
  receiveTemplateBlocks();
111
  } );
112
 
113
+ useEffect( () => {
114
+ if ( isSelected ) {
115
+ if ( ! isEditorSidebarOpened ) {
116
+ setState( { shouldCloseSidebarOnSelect: false } );
117
+ } else if ( shouldCloseSidebarOnSelect ) {
118
+ closeGeneralSidebar();
119
+ } else {
120
+ clearSelectedBlock();
121
+ }
122
+ } else {
123
+ setState( { shouldCloseSidebarOnSelect: true } );
124
+ }
125
+ } );
126
+
127
+ const { align, className } = attributes;
128
+
129
+ const save = event => {
130
+ if ( ! isDirty ) {
131
+ return;
132
+ }
133
+ event.preventDefault();
134
+ setNavigateToTemplate( true );
135
+ savePost();
136
+ };
137
 
138
  return (
139
  <div
140
+ className={ classNames( 'template-block', className, {
141
  [ `align${ align }` ]: align,
142
  } ) }
143
  >
153
  setAttributes={ noop }
154
  />
155
  </Disabled>
156
+ { isSelected && (
157
+ <Placeholder className="template-block__overlay">
158
+ <Button href={ editTemplateUrl } onClick={ save } isDefault ref={ navButton }>
159
+ { navigateToTemplate ? <Spinner /> : sprintf( __( 'Edit %s' ), templateTitle ) }
160
+ </Button>
161
+ </Placeholder>
162
+ ) }
 
 
 
163
  </Fragment>
164
  ) }
165
  </div>
full-site-editing/blocks/template/index.js CHANGED
@@ -4,22 +4,29 @@
4
  */
5
  import { registerBlockType } from '@wordpress/blocks';
6
  import { __ } from '@wordpress/i18n';
 
 
7
 
8
  /**
9
  * Internal dependencies
10
  */
11
  import edit from './edit';
12
  import './style.scss';
 
13
 
14
- if ( 'wp_template_part' !== fullSiteEditing.editorPostType ) {
15
  registerBlockType( 'a8c/template', {
16
- title: __( 'Template Part' ),
17
- description: __( 'Display a template part.' ),
18
  icon: 'layout',
19
  category: 'layout',
20
- attributes: { templateId: { type: 'number' } },
 
 
 
21
  supports: {
22
- anchor: true,
 
23
  html: false,
24
  reusable: false,
25
  },
@@ -30,3 +37,21 @@ if ( 'wp_template_part' !== fullSiteEditing.editorPostType ) {
30
  },
31
  } );
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  */
5
  import { registerBlockType } from '@wordpress/blocks';
6
  import { __ } from '@wordpress/i18n';
7
+ import { createHigherOrderComponent } from '@wordpress/compose';
8
+ import { addFilter } from '@wordpress/hooks';
9
 
10
  /**
11
  * Internal dependencies
12
  */
13
  import edit from './edit';
14
  import './style.scss';
15
+ import './site-logo';
16
 
17
+ if ( 'wp_template' !== fullSiteEditing.editorPostType ) {
18
  registerBlockType( 'a8c/template', {
19
+ title: __( 'Template' ),
20
+ description: __( 'Display a template.' ),
21
  icon: 'layout',
22
  category: 'layout',
23
+ attributes: {
24
+ templateId: { type: 'number' },
25
+ className: { type: 'string' },
26
+ },
27
  supports: {
28
+ anchor: false,
29
+ customClassName: false,
30
  html: false,
31
  reusable: false,
32
  },
37
  },
38
  } );
39
  }
40
+
41
+ const addFSETemplateClassname = createHigherOrderComponent( BlockListBlock => {
42
+ return props => {
43
+ if ( props.name !== 'a8c/template' ) {
44
+ return <BlockListBlock { ...props } />;
45
+ }
46
+
47
+ return <BlockListBlock { ...props } className="template__block-container" />;
48
+ };
49
+ }, 'addFSETemplateClassname' );
50
+
51
+ // Must be 9 or this breaks on Simple Sites
52
+ addFilter(
53
+ 'editor.BlockListBlock',
54
+ 'full-site-editing/blocks/template',
55
+ addFSETemplateClassname,
56
+ 9
57
+ );
full-site-editing/blocks/template/index.php CHANGED
@@ -2,9 +2,11 @@
2
  /**
3
  * Render template block file.
4
  *
5
- * @package full-site-editing
6
  */
7
 
 
 
8
  /**
9
  * Renders template.
10
  *
@@ -24,12 +26,12 @@ function render_template_block( $attributes ) {
24
  ob_start();
25
  ?>
26
 
27
- <div class="template-part<?php echo esc_attr( $align ); ?>">
28
  <?php
29
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
30
  echo apply_filters( 'the_content', get_the_content() );
31
  ?>
32
- </div><!-- .template-part -->
33
 
34
  <?php
35
  $content = ob_get_clean();
2
  /**
3
  * Render template block file.
4
  *
5
+ * @package A8C\FSE
6
  */
7
 
8
+ namespace A8C\FSE;
9
+
10
  /**
11
  * Renders template.
12
  *
26
  ob_start();
27
  ?>
28
 
29
+ <div class="template<?php echo esc_attr( $align ); ?>">
30
  <?php
31
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
32
  echo apply_filters( 'the_content', get_the_content() );
33
  ?>
34
+ </div><!-- .template -->
35
 
36
  <?php
37
  $content = ob_get_clean();
full-site-editing/blocks/template/site-logo.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint-disable wpcalypso/import-docblock */
2
+ /**
3
+ * WordPress dependencies
4
+ */
5
+ import { createHigherOrderComponent } from '@wordpress/compose';
6
+ import { addFilter } from '@wordpress/hooks';
7
+
8
+ const addFSESiteLogoClassname = createHigherOrderComponent( BlockListBlock => {
9
+ return props => {
10
+ if ( props.attributes.className !== 'fse-site-logo' ) {
11
+ return <BlockListBlock { ...props } />;
12
+ }
13
+
14
+ return <BlockListBlock { ...props } className="template__site-logo" />;
15
+ };
16
+ }, 'addFSESiteLogoClassname' );
17
+
18
+ addFilter( 'editor.BlockListBlock', 'full-site-editing/blocks/template', addFSESiteLogoClassname );
full-site-editing/blocks/template/style.scss CHANGED
@@ -1,8 +1,8 @@
1
  .template-block {
2
  min-height: 200px;
3
-
4
- &:hover .template-block__overlay {
5
- display: flex;
6
  }
7
  }
8
 
@@ -29,15 +29,67 @@
29
  }
30
 
31
  .template-block__overlay {
32
- display: none;
33
  position: absolute;
34
  top: 0;
35
- left: 0;
36
  width: 100%;
37
  height: 100%;
38
- background: rgba( #000000, 0.75 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- .components-placeholder__instructions {
41
- color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
  }
 
 
 
 
 
 
 
 
 
1
  .template-block {
2
  min-height: 200px;
3
+
4
+ .components-button .components-spinner {
5
+ margin-top: 4px;
6
  }
7
  }
8
 
29
  }
30
 
31
  .template-block__overlay {
32
+ display: flex;
33
  position: absolute;
34
  top: 0;
35
+ left: 2px;
36
  width: 100%;
37
  height: 100%;
38
+ justify-content: center;
39
+ align-items: center;
40
+ background: rgba( #f5f5f5, 0.8 );
41
+ z-index: 2;
42
+ }
43
+
44
+ .block-editor-block-list__layout {
45
+ .template__block-container {
46
+ .wp-block {
47
+ margin-top: 15px;
48
+ margin-bottom: 15px;
49
+ }
50
+
51
+ &.is-hovered {
52
+ cursor: pointer;
53
+ }
54
 
55
+ &.is-selected {
56
+ // Hide the toolbar for this block
57
+ .block-editor-block-contextual-toolbar {
58
+ display: none;
59
+ }
60
+
61
+ .components-disabled {
62
+ filter: blur( 2px );
63
+ }
64
+ }
65
+
66
+ &.block-editor-block-list__block {
67
+ // Need to get super specific to override the core css selectors:
68
+ &,
69
+ &.has-child-selected,
70
+ &.is-hovered,
71
+ &.is-navigate-mode {
72
+ > .block-editor-block-list__block-edit {
73
+ &::before {
74
+ transition: none;
75
+ border: none;
76
+ outline: none;
77
+ box-shadow: none;
78
+ }
79
+ > .block-editor-block-list__breadcrumb {
80
+ display: none;
81
+ }
82
+ }
83
+ }
84
+ }
85
  }
86
  }
87
+
88
+ // don't display the site logo action buttons if not editing the template
89
+ .block-editor-page:not( .post-type-wp_template ) {
90
+ .fse-site-logo {
91
+ .components-placeholder__fieldset, .components-placeholder__instructions {
92
+ display: none;
93
+ }
94
+ }
95
+ }
full-site-editing/class-full-site-editing.php CHANGED
@@ -2,9 +2,11 @@
2
  /**
3
  * Full site editing file.
4
  *
5
- * @package full-site-editing
6
  */
7
 
 
 
8
  /**
9
  * Class Full_Site_Editing
10
  */
@@ -12,36 +14,67 @@ class Full_Site_Editing {
12
  /**
13
  * Class instance.
14
  *
15
- * @var Full_Site_Editing
16
  */
17
  private static $instance = null;
18
 
19
  /**
20
  * Custom post types.
21
  *
22
- * @var Full_Site_Editing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  */
24
- private $template_post_types = array( 'wp_template', 'wp_template_part' );
 
 
 
 
 
 
 
25
 
26
  /**
27
  * Full_Site_Editing constructor.
28
  */
29
  private function __construct() {
30
- add_action( 'init', array( $this, 'register_blocks' ), 100 );
31
- add_action( 'init', array( $this, 'register_template_post_types' ) );
32
- add_action( 'init', array( $this, 'register_meta_template_id' ) );
33
- add_action( 'rest_api_init', array( $this, 'allow_searching_for_templates' ) );
34
- add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_script_and_style' ), 100 );
35
- add_action( 'the_post', array( $this, 'merge_template_and_post' ) );
36
- add_filter( 'wp_insert_post_data', array( $this, 'remove_template_components' ), 10, 2 );
37
- add_filter( 'admin_body_class', array( $this, 'toggle_editor_post_title_visibility' ) );
38
- add_filter( 'block_editor_settings', array( $this, 'set_block_template' ) );
 
 
 
 
 
 
 
 
 
 
39
  }
40
 
41
  /**
42
  * Creates instance.
43
  *
44
- * @return \Full_Site_Editing
45
  */
46
  public static function get_instance() {
47
  if ( null === self::$instance ) {
@@ -52,216 +85,61 @@ class Full_Site_Editing {
52
  }
53
 
54
  /**
55
- * Register post types.
 
 
 
 
56
  */
57
- public function register_template_post_types() {
58
- register_post_type(
59
- 'wp_template',
60
- array(
61
- 'labels' => array(
62
- 'name' => _x( 'Templates', 'post type general name', 'full-site-editing' ),
63
- 'singular_name' => _x( 'Template', 'post type singular name', 'full-site-editing' ),
64
- 'menu_name' => _x( 'Templates', 'admin menu', 'full-site-editing' ),
65
- 'name_admin_bar' => _x( 'Template', 'add new on admin bar', 'full-site-editing' ),
66
- 'add_new' => _x( 'Add New', 'Template', 'full-site-editing' ),
67
- 'add_new_item' => __( 'Add New Template', 'full-site-editing' ),
68
- 'new_item' => __( 'New Template', 'full-site-editing' ),
69
- 'edit_item' => __( 'Edit Template', 'full-site-editing' ),
70
- 'view_item' => __( 'View Template', 'full-site-editing' ),
71
- 'all_items' => __( 'All Templates', 'full-site-editing' ),
72
- 'search_items' => __( 'Search Templates', 'full-site-editing' ),
73
- 'not_found' => __( 'No templates found.', 'full-site-editing' ),
74
- 'not_found_in_trash' => __( 'No templates found in Trash.', 'full-site-editing' ),
75
- 'filter_items_list' => __( 'Filter templates list', 'full-site-editing' ),
76
- 'items_list_navigation' => __( 'Templates list navigation', 'full-site-editing' ),
77
- 'items_list' => __( 'Templates list', 'full-site-editing' ),
78
- 'item_published' => __( 'Template published.', 'full-site-editing' ),
79
- 'item_published_privately' => __( 'Template published privately.', 'full-site-editing' ),
80
- 'item_reverted_to_draft' => __( 'Template reverted to draft.', 'full-site-editing' ),
81
- 'item_scheduled' => __( 'Template scheduled.', 'full-site-editing' ),
82
- 'item_updated' => __( 'Template updated.', 'full-site-editing' ),
83
- ),
84
- 'menu_icon' => 'dashicons-layout',
85
- 'public' => false,
86
- 'show_ui' => true,
87
- 'show_in_menu' => true,
88
- 'rewrite' => false,
89
- 'show_in_rest' => true,
90
- 'rest_base' => 'templates',
91
- 'rest_controller_class' => 'A8C_REST_Templates_Controller',
92
- 'capability_type' => 'template',
93
- 'capabilities' => array(
94
- // You need to be able to edit posts, in order to read templates in their raw form.
95
- 'read' => 'edit_posts',
96
- // You need to be able to customize, in order to create templates.
97
- 'create_posts' => 'edit_theme_options',
98
- 'edit_posts' => 'edit_theme_options',
99
- 'delete_posts' => 'edit_theme_options',
100
- 'edit_published_posts' => 'edit_theme_options',
101
- 'delete_published_posts' => 'edit_theme_options',
102
- 'edit_others_posts' => 'edit_theme_options',
103
- 'delete_others_posts' => 'edit_theme_options',
104
- 'publish_posts' => 'edit_theme_options',
105
- ),
106
- 'map_meta_cap' => true,
107
- 'supports' => array(
108
- 'title',
109
- 'editor',
110
- ),
111
- )
112
- );
113
 
114
- register_post_type(
115
- 'wp_template_part',
116
- array(
117
- 'labels' => array(
118
- 'name' => _x( 'Template Parts', 'post type general name', 'full-site-editing' ),
119
- 'singular_name' => _x( 'Template Part', 'post type singular name', 'full-site-editing' ),
120
- 'menu_name' => _x( 'Template Parts', 'admin menu', 'full-site-editing' ),
121
- 'name_admin_bar' => _x( 'Template Part', 'add new on admin bar', 'full-site-editing' ),
122
- 'add_new' => _x( 'Add New', 'Template Part', 'full-site-editing' ),
123
- 'add_new_item' => __( 'Add New Template Part', 'full-site-editing' ),
124
- 'new_item' => __( 'New Template Part', 'full-site-editing' ),
125
- 'edit_item' => __( 'Edit Template Part', 'full-site-editing' ),
126
- 'view_item' => __( 'View Template Part', 'full-site-editing' ),
127
- 'all_items' => __( 'All Template Parts', 'full-site-editing' ),
128
- 'search_items' => __( 'Search Template Parts', 'full-site-editing' ),
129
- 'not_found' => __( 'No template parts found.', 'full-site-editing' ),
130
- 'not_found_in_trash' => __( 'No template parts found in Trash.', 'full-site-editing' ),
131
- 'filter_items_list' => __( 'Filter template parts list', 'full-site-editing' ),
132
- 'items_list_navigation' => __( 'Template parts list navigation', 'full-site-editing' ),
133
- 'items_list' => __( 'Template parts list', 'full-site-editing' ),
134
- 'item_published' => __( 'Template part published.', 'full-site-editing' ),
135
- 'item_published_privately' => __( 'Template part published privately.', 'full-site-editing' ),
136
- 'item_reverted_to_draft' => __( 'Template part reverted to draft.', 'full-site-editing' ),
137
- 'item_scheduled' => __( 'Template part scheduled.', 'full-site-editing' ),
138
- 'item_updated' => __( 'Template part updated.', 'full-site-editing' ),
139
- ),
140
- 'menu_icon' => 'dashicons-layout',
141
- 'public' => false,
142
- 'show_ui' => true,
143
- 'show_in_menu' => true,
144
- 'rewrite' => false,
145
- 'show_in_rest' => true,
146
- 'rest_base' => 'template_parts',
147
- 'rest_controller_class' => 'A8C_REST_Templates_Controller',
148
- 'capability_type' => 'template_part',
149
- 'capabilities' => array(
150
- // You need to be able to edit posts, in order to read templates in their raw form.
151
- 'read' => 'edit_posts',
152
- // You need to be able to customize, in order to create templates.
153
- 'create_posts' => 'edit_theme_options',
154
- 'edit_posts' => 'edit_theme_options',
155
- 'delete_posts' => 'edit_theme_options',
156
- 'edit_published_posts' => 'edit_theme_options',
157
- 'delete_published_posts' => 'edit_theme_options',
158
- 'edit_others_posts' => 'edit_theme_options',
159
- 'delete_others_posts' => 'edit_theme_options',
160
- 'publish_posts' => 'edit_theme_options',
161
- ),
162
- 'map_meta_cap' => true,
163
- 'supports' => array(
164
- 'title',
165
- 'editor',
166
- ),
167
- )
168
- );
169
 
170
- register_taxonomy(
171
- 'wp_template_type',
172
- 'wp_template',
173
- array(
174
- 'labels' => array(
175
- 'name' => _x( 'Template Types', 'taxonomy general name', 'full-site-editing' ),
176
- 'singular_name' => _x( 'Template Type', 'taxonomy singular name', 'full-site-editing' ),
177
- 'menu_name' => _x( 'Template Types', 'admin menu', 'full-site-editing' ),
178
- 'all_items' => __( 'All Template Types', 'full-site-editing' ),
179
- 'edit_item' => __( 'Edit Template Type', 'full-site-editing' ),
180
- 'view_item' => __( 'View Template Type', 'full-site-editing' ),
181
- 'update_item' => __( 'Update Template Type', 'full-site-editing' ),
182
- 'add_new_item' => __( 'Add New Template Type', 'full-site-editing' ),
183
- 'new_item_name' => __( 'New Template Type', 'full-site-editing' ),
184
- 'parent_item' => __( 'Parent Template Type', 'full-site-editing' ),
185
- 'parent_item_colon' => __( 'Parent Template Type:', 'full-site-editing' ),
186
- 'search_items' => __( 'Search Template Types', 'full-site-editing' ),
187
- 'not_found' => __( 'No template types found.', 'full-site-editing' ),
188
- 'back_to_items' => __( 'Back to template types', 'full-site-editing' ),
189
- ),
190
- 'public' => false,
191
- 'publicly_queryable' => true,
192
- 'show_ui' => true,
193
- 'show_in_menu' => false,
194
- 'show_in_nav_menu' => false,
195
- 'show_in_rest' => true,
196
- 'rest_base' => 'template_types',
197
- 'show_tagcloud' => false,
198
- 'show_admin_column' => true,
199
- 'hierarchical' => true,
200
- 'rewrite' => false,
201
- 'capabilities' => array(
202
- 'manage_terms' => 'edit_theme_options',
203
- 'edit_terms' => 'edit_theme_options',
204
- 'delete_terms' => 'edit_theme_options',
205
- 'assign_terms' => 'edit_theme_options',
206
- ),
207
- )
208
- );
209
 
210
- register_taxonomy(
211
- 'wp_template_part_type',
212
- 'wp_template_part',
213
- array(
214
- 'labels' => array(
215
- 'name' => _x( 'Template Part Types', 'taxonomy general name', 'full-site-editing' ),
216
- 'singular_name' => _x( 'Template Part Type', 'taxonomy singular name', 'full-site-editing' ),
217
- 'menu_name' => _x( 'Template Part Types', 'admin menu', 'full-site-editing' ),
218
- 'all_items' => __( 'All Template Part Types', 'full-site-editing' ),
219
- 'edit_item' => __( 'Edit Template Part Type', 'full-site-editing' ),
220
- 'view_item' => __( 'View Template Part Type', 'full-site-editing' ),
221
- 'update_item' => __( 'Update Template Part Type', 'full-site-editing' ),
222
- 'add_new_item' => __( 'Add New Template Part Type', 'full-site-editing' ),
223
- 'new_item_name' => __( 'New Template Part Type', 'full-site-editing' ),
224
- 'parent_item' => __( 'Parent Template Part Type', 'full-site-editing' ),
225
- 'parent_item_colon' => __( 'Parent Template Part Type:', 'full-site-editing' ),
226
- 'search_items' => __( 'Search Template Part Types', 'full-site-editing' ),
227
- 'not_found' => __( 'No template part types found.', 'full-site-editing' ),
228
- 'back_to_items' => __( 'Back to template part types', 'full-site-editing' ),
229
- ),
230
- 'public' => false,
231
- 'publicly_queryable' => true,
232
- 'show_ui' => true,
233
- 'show_in_menu' => false,
234
- 'show_in_nav_menu' => false,
235
- 'show_in_rest' => true,
236
- 'rest_base' => 'template_part_types',
237
- 'show_tagcloud' => false,
238
- 'show_admin_column' => true,
239
- 'hierarchical' => true,
240
- 'rewrite' => false,
241
- 'capabilities' => array(
242
- 'manage_terms' => 'edit_theme_options',
243
- 'edit_terms' => 'edit_theme_options',
244
- 'delete_terms' => 'edit_theme_options',
245
- 'assign_terms' => 'edit_theme_options',
246
- ),
247
- )
248
- );
249
  }
250
 
251
  /**
252
- * Register post meta.
 
 
 
 
 
 
 
 
253
  */
254
- public function register_meta_template_id() {
255
- register_post_meta(
256
- '',
257
- '_wp_template_id',
258
- array(
259
- 'auth_callback' => array( $this, 'meta_template_id_auth_callback' ),
260
- 'show_in_rest' => true,
261
- 'single' => true,
262
- 'type' => 'integer',
263
- )
264
- );
 
 
265
  }
266
 
267
  /**
@@ -291,16 +169,14 @@ class Full_Site_Editing {
291
  true
292
  );
293
 
294
- $feature_flags = A8C_Full_Site_Editing_Feature_Flags::get_instance();
295
-
296
  wp_localize_script(
297
  'a8c-full-site-editing-script',
298
  'fullSiteEditing',
299
  array(
300
- 'editorPostType' => get_current_screen()->post_type,
301
- 'featureFlags' => $feature_flags->get_flags(),
302
- 'closeButtonUrl' => esc_url( $this->get_close_button_url() ),
303
- 'editTemplatePartBaseUrl' => esc_url( $this->get_edit_template_part_base_url() ),
304
  )
305
  );
306
 
@@ -323,77 +199,87 @@ class Full_Site_Editing {
323
  'a8c/navigation-menu',
324
  array(
325
  'attributes' => [
326
- 'className' => [
327
  'default' => '',
328
  'type' => 'string',
329
  ],
330
  ],
331
- 'render_callback' => 'a8c_fse_render_navigation_menu_block',
332
  )
333
  );
334
 
335
  register_block_type(
336
  'a8c/post-content',
337
  array(
338
- 'render_callback' => 'render_post_content_block',
339
  )
340
  );
341
 
342
  register_block_type(
343
  'a8c/site-description',
344
  array(
345
- 'render_callback' => 'render_site_description_block',
346
  )
347
  );
348
 
349
  register_block_type(
350
  'a8c/template',
351
  array(
352
- 'render_callback' => 'render_template_block',
353
- )
354
- );
355
-
356
- register_block_type(
357
- 'a8c/site-logo',
358
- array(
359
- 'attributes' => array(
360
- 'editorPreview' => array(
361
- 'type' => 'boolean',
362
- 'default' => false,
363
- ),
364
- ),
365
- 'render_callback' => 'render_site_logo',
366
  )
367
  );
368
 
369
  register_block_type(
370
  'a8c/site-title',
371
  array(
372
- 'render_callback' => 'render_site_title_block',
373
  )
374
  );
375
  }
376
 
377
  /**
378
- * This will set the `wp_template` and `wp_template_part` post types to `public` to support
379
- * the core search endpoint, which looks for it.
 
 
380
  */
381
- public function allow_searching_for_templates() {
382
- $post_type = get_post_type_object( 'wp_template' );
383
- if ( ! ( $post_type instanceof WP_Post_Type ) ) {
384
- return;
385
  }
386
 
387
- // Setting this to `public` will allow it to be found in the search endpoint.
388
- $post_type->public = true;
389
 
390
- $post_type = get_post_type_object( 'wp_template_part' );
391
- if ( ! ( $post_type instanceof WP_Post_Type ) ) {
392
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  }
394
 
395
- // Setting this to `public` will allow it to be found in the search endpoint.
396
- $post_type->public = true;
 
 
 
397
  }
398
 
399
  /**
@@ -401,27 +287,21 @@ class Full_Site_Editing {
401
  *
402
  * In some cases we want to override the default value which would take us to post listing
403
  * for a given post type. For example, when navigating back from Header, we want to show the
404
- * parent page editing view, and not the Template Part CPT list.
405
  *
406
  * @return null|string Override URL string if it should be inserted, or null otherwise.
407
  */
408
  public function get_close_button_url() {
409
- // phpcs:disable WordPress.Security.NonceVerification.Recommended
410
- if ( ! isset( $_GET['fse_parent_post'] ) ) {
411
- return null;
412
- }
413
 
414
- $parent_post_id = absint( $_GET['fse_parent_post'] );
415
- // phpcs:enable WordPress.Security.NonceVerification.Recommended
416
-
417
- if ( empty( $parent_post_id ) ) {
418
  return null;
419
  }
420
 
421
  $close_button_url = get_edit_post_link( $parent_post_id );
422
 
423
  /**
424
- * Filter the Gutenberg's close button URL when editing Template Part CPTs.
425
  *
426
  * @since 0.1
427
  *
@@ -431,29 +311,29 @@ class Full_Site_Editing {
431
  }
432
 
433
  /**
434
- * Returns the base URL for the Edit Template Part button. The URL does not contain neither
435
- * the post ID nor the template part ID. Those query arguments should be provided by
436
- * the Template Part on the Block.
437
  *
438
  * @return string edit link without post ID
439
  */
440
- public function get_edit_template_part_base_url() {
441
  $edit_post_link = remove_query_arg( 'post', get_edit_post_link( 0, 'edit' ) );
442
 
443
  /**
444
- * Filter the Gutenberg's edit template part button base URL
445
  * when editing pages or posts.
446
  *
447
  * @since 0.2
448
  *
449
  * @param string Current edit button URL.
450
  */
451
- return apply_filters( 'a8c_fse_edit_template_part_base_url', $edit_post_link );
452
  }
453
 
454
  /** This will merge the post content with the post template, modifiying the $post parameter.
455
  *
456
- * @param WP_Post $post Post instance.
457
  */
458
  public function merge_template_and_post( $post ) {
459
  // Bail if not a REST API Request.
@@ -466,8 +346,8 @@ class Full_Site_Editing {
466
  return;
467
  }
468
 
469
- $template = new A8C_WP_Template( $post->ID );
470
- $template_content = $template->get_template_content();
471
 
472
  // Bail if the template has no post content block.
473
  if ( ! has_block( 'a8c/post-content', $template_content ) ) {
@@ -510,18 +390,6 @@ class Full_Site_Editing {
510
  return $data;
511
  }
512
 
513
- /**
514
- * Determine if the current edited post is a full site page.
515
- * If it's a page being loaded that has a `wp_template`, it's a page that our FSE plugin should handle.
516
- *
517
- * @return boolean
518
- */
519
- public function is_full_site_page() {
520
- $fse_template = new A8C_WP_Template();
521
-
522
- return 'page' === get_post_type() && $fse_template->get_template_id();
523
- }
524
-
525
  /**
526
  * Return an extra class that will be assigned to the body element if a full site page is being edited.
527
  *
@@ -546,100 +414,130 @@ class Full_Site_Editing {
546
  */
547
  public function set_block_template( $editor_settings ) {
548
  if ( $this->is_full_site_page() ) {
549
- $fse_template = new A8C_WP_Template();
550
  $template_blocks = $fse_template->get_template_blocks();
551
 
552
  $template = array();
553
  foreach ( $template_blocks as $block ) {
554
- $template[] = fse_map_block_to_editor_template_setting( $block );
555
  }
556
  $editor_settings['template'] = $template;
557
  $editor_settings['templateLock'] = 'all';
558
  }
559
  return $editor_settings;
560
  }
561
- }
562
 
563
- /**
564
- * Returns an array with the expected format of the block template setting syntax.
565
- *
566
- * @see https://github.com/WordPress/gutenberg/blob/1414cf0ad1ec3d0f3e86a40815513c15938bb522/docs/designers-developers/developers/block-api/block-templates.md
567
- *
568
- * @param array $block Block to convert.
569
- * @return array
570
- */
571
- function fse_map_block_to_editor_template_setting( $block ) {
572
- $block_name = $block['blockName'];
573
- $attrs = $block['attrs'];
574
- $inner_blocks = $block['innerBlocks'];
575
-
576
- $inner_blocks_template = array();
577
- foreach ( $inner_blocks as $inner_block ) {
578
- $inner_blocks[] = fse_map_block_to_editor_template_setting( $inner_block );
 
 
 
579
  }
580
- return array( $block_name, $attrs, $inner_blocks_template );
581
- }
582
 
583
- if ( ! function_exists( 'serialize_block' ) ) {
584
  /**
585
- * Renders an HTML-serialized form of a block object
586
- * from https://core.trac.wordpress.org/ticket/47375
587
  *
588
- * Should be available since WordPress 5.3.0.
589
  *
590
- * @param array $block The block being rendered.
591
- * @return string The HTML-serialized form of the block
592
  */
593
- function serialize_block( $block ) {
594
- // Non-block content has no block name.
595
- if ( null === $block['blockName'] ) {
596
- return $block['innerHTML'];
 
 
 
 
597
  }
 
 
598
 
599
- $unwanted = array( '--', '<', '>', '&', '\"' );
600
- $wanted = array( '\u002d\u002d', '\u003c', '\u003e', '\u0026', '\u0022' );
601
-
602
- $name = 0 === strpos( $block['blockName'], 'core/' ) ? substr( $block['blockName'], 5 ) : $block['blockName'];
603
- $has_attrs = ! empty( $block['attrs'] );
604
- $attrs = $has_attrs ? str_replace( $unwanted, $wanted, wp_json_encode( $block['attrs'] ) ) : '';
605
-
606
- // Early abort for void blocks holding no content.
607
- if ( empty( $block['innerContent'] ) ) {
608
- return $has_attrs
609
- ? "<!-- wp:{$name} {$attrs} /-->"
610
- : "<!-- wp:{$name} /-->";
611
  }
 
 
612
 
613
- $output = $has_attrs
614
- ? "<!-- wp:{$name} {$attrs} -->\n"
615
- : "<!-- wp:{$name} -->\n";
616
-
617
- $inner_block_index = 0;
618
- foreach ( $block['innerContent'] as $chunk ) {
619
- $output .= null === $chunk
620
- ? serialize_block( $block['innerBlocks'][ $inner_block_index++ ] )
621
- : $chunk;
 
622
 
623
- $output .= "\n";
 
 
 
 
 
 
 
624
  }
 
625
 
626
- $output .= "<!-- /wp:{$name} -->";
627
-
628
- return $output;
 
 
 
 
 
 
 
 
 
629
  }
630
- }
631
 
632
- if ( ! function_exists( 'serialize_blocks' ) ) {
633
  /**
634
- * Renders an HTML-serialized form of a list of block objects
635
- * from https://core.trac.wordpress.org/ticket/47375
636
  *
637
- * Should be available since WordPress 5.3.0.
 
 
 
 
 
 
 
 
 
638
  *
639
- * @param array $blocks The list of parsed block objects.
640
- * @return string The HTML-serialized form of the list of blocks.
641
  */
642
- function serialize_blocks( $blocks ) {
643
- return implode( "\n\n", array_map( 'serialize_block', $blocks ) );
 
 
644
  }
645
  }
2
  /**
3
  * Full site editing file.
4
  *
5
+ * @package A8C\FSE
6
  */
7
 
8
+ namespace A8C\FSE;
9
+
10
  /**
11
  * Class Full_Site_Editing
12
  */
14
  /**
15
  * Class instance.
16
  *
17
+ * @var \A8C\FSE\Full_Site_Editing
18
  */
19
  private static $instance = null;
20
 
21
  /**
22
  * Custom post types.
23
  *
24
+ * @var array
25
+ */
26
+ private $template_post_types = [ 'wp_template' ];
27
+
28
+ /**
29
+ * Current theme slug.
30
+ *
31
+ * @var string
32
+ */
33
+ private $theme_slug = '';
34
+
35
+ /**
36
+ * Instance of WP_Template_Inserter class.
37
+ *
38
+ * @var WP_Template_Inserter
39
  */
40
+ public $wp_template_inserter;
41
+
42
+ /**
43
+ * List of FSE supported themes.
44
+ *
45
+ * @var array
46
+ */
47
+ const SUPPORTED_THEMES = [ 'modern-business' ];
48
 
49
  /**
50
  * Full_Site_Editing constructor.
51
  */
52
  private function __construct() {
53
+ add_action( 'init', [ $this, 'register_blocks' ], 100 );
54
+ add_action( 'init', [ $this, 'register_template_post_types' ] );
55
+ add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_script_and_style' ], 100 );
56
+ add_action( 'the_post', [ $this, 'merge_template_and_post' ] );
57
+ add_filter( 'wp_insert_post_data', [ $this, 'remove_template_components' ], 10, 2 );
58
+ add_filter( 'admin_body_class', [ $this, 'toggle_editor_post_title_visibility' ] );
59
+ add_filter( 'block_editor_settings', [ $this, 'set_block_template' ] );
60
+ add_action( 'after_switch_theme', [ $this, 'insert_default_data' ] );
61
+ add_filter( 'body_class', array( $this, 'add_fse_body_class' ) );
62
+
63
+ add_filter( 'post_row_actions', [ $this, 'remove_trash_row_action_for_template_post_types' ], 10, 2 );
64
+ add_filter( 'bulk_actions-edit-wp_template', [ $this, 'remove_trash_bulk_action_for_template_post_type' ] );
65
+ add_action( 'wp_trash_post', [ $this, 'restrict_template_deletion' ] );
66
+ add_filter( 'wp_template_type_row_actions', [ $this, 'remove_delete_row_action_for_template_taxonomy' ], 10, 2 );
67
+ add_filter( 'bulk_actions-edit-wp_template_type', [ $this, 'remove_delete_bulk_action_for_template_taxonomy' ] );
68
+ add_action( 'pre_delete_term', [ $this, 'restrict_template_taxonomy_deletion' ], 10, 2 );
69
+
70
+ $this->theme_slug = $this->normalize_theme_slug( get_stylesheet() );
71
+ $this->wp_template_inserter = new WP_Template_Inserter( $this->theme_slug );
72
  }
73
 
74
  /**
75
  * Creates instance.
76
  *
77
+ * @return \A8C\FSE\Full_Site_Editing
78
  */
79
  public static function get_instance() {
80
  if ( null === self::$instance ) {
85
  }
86
 
87
  /**
88
+ * Determines whether provided theme supports FSE.
89
+ *
90
+ * @param string $theme_slug Theme slug to check support for.
91
+ *
92
+ * @return bool True if passed theme supports FSE, false otherwise.
93
  */
94
+ public function is_supported_theme( $theme_slug ) {
95
+ return in_array( $theme_slug, self::SUPPORTED_THEMES, true );
96
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ /**
99
+ * Inserts template data for the theme we are currently switching to.
100
+ *
101
+ * This insertion will only happen if theme supports FSE.
102
+ * It is hooked into after_switch_theme action.
103
+ */
104
+ public function insert_default_data() {
105
+ // Bail if theme doesn't support FSE.
106
+ if ( ! $this->is_supported_theme( $this->theme_slug ) ) {
107
+ return;
108
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
+ if ( ! $this->wp_template_inserter->is_template_data_inserted() ) {
111
+ $this->wp_template_inserter->insert_default_template_data();
112
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ if ( ! $this->wp_template_inserter->is_pages_data_inserted() ) {
115
+ $this->wp_template_inserter->insert_default_pages();
116
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
 
119
  /**
120
+ * Returns normalized theme slug for the current theme.
121
+ *
122
+ * Normalize WP.com theme slugs that differ from those that we'll get on self hosted sites.
123
+ * For example, we will get 'modern-business' when retrieving theme slug on self hosted sites,
124
+ * but due to WP.com setup, on Simple sites we'll get 'pub/modern-business' for the theme.
125
+ *
126
+ * @param string $theme_slug Theme slug to check support for.
127
+ *
128
+ * @return string Normalized theme slug.
129
  */
130
+ public function normalize_theme_slug( $theme_slug ) {
131
+ if ( 'pub/' === substr( $theme_slug, 0, 4 ) ) {
132
+ $theme_slug = str_replace( 'pub/', '', $theme_slug );
133
+ }
134
+
135
+ return $theme_slug;
136
+ }
137
+
138
+ /**
139
+ * Register post types.
140
+ */
141
+ public function register_template_post_types() {
142
+ $this->wp_template_inserter->register_template_post_types();
143
  }
144
 
145
  /**
169
  true
170
  );
171
 
 
 
172
  wp_localize_script(
173
  'a8c-full-site-editing-script',
174
  'fullSiteEditing',
175
  array(
176
+ 'editorPostType' => get_current_screen()->post_type,
177
+ 'closeButtonLabel' => $this->get_close_button_label(),
178
+ 'closeButtonUrl' => esc_url( $this->get_close_button_url() ),
179
+ 'editTemplateBaseUrl' => esc_url( $this->get_edit_template_base_url() ),
180
  )
181
  );
182
 
199
  'a8c/navigation-menu',
200
  array(
201
  'attributes' => [
202
+ 'className' => [
203
  'default' => '',
204
  'type' => 'string',
205
  ],
206
  ],
207
+ 'render_callback' => __NAMESPACE__ . '\render_navigation_menu_block',
208
  )
209
  );
210
 
211
  register_block_type(
212
  'a8c/post-content',
213
  array(
214
+ 'render_callback' => __NAMESPACE__ . '\render_post_content_block',
215
  )
216
  );
217
 
218
  register_block_type(
219
  'a8c/site-description',
220
  array(
221
+ 'render_callback' => __NAMESPACE__ . '\render_site_description_block',
222
  )
223
  );
224
 
225
  register_block_type(
226
  'a8c/template',
227
  array(
228
+ 'render_callback' => __NAMESPACE__ . '\render_template_block',