Automattic / create-content-model

Create content models from the UI
GNU General Public License v2.0
160 stars 7 forks source link

Lock blocks from the back-end to prevent unwanted edits #127

Open zaguiini opened 2 months ago

zaguiini commented 2 months ago

During our prototype we've added a content locking mechanism to prevent the users from modifying the template when entering data.

We're doing it from the front-end, which creates unwanted edits and prevents the user from leaving the screen if there are no changes to the post.

Let's do it from the back-end to prevent these edits. After we move, we can safely delete the useContentLocking hook and references to it.

Proposed implementation

Fill in the TODO: lock blocks here. part.

diff --git a/includes/runtime/class-content-model-block.php b/includes/runtime/class-content-model-block.php
index 7511bb4..e159127 100644
--- a/includes/runtime/class-content-model-block.php
+++ b/includes/runtime/class-content-model-block.php
@@ -217,7 +217,7 @@ final class Content_Model_Block {
        remove_filter( 'pre_render_block', array( $this, 'render_group_variation' ), 99 );

        // Accessing index zero because we've passed an array with one element above.
-       $result = render_block( $hydrator->hydrate()[0] );
+       $result = render_block( $hydrator->hydrate_groups()[0] );

        add_filter( 'pre_render_block', array( $this, 'render_group_variation' ), 99, 2 );

diff --git a/includes/runtime/class-content-model-data-hydrator.php b/includes/runtime/class-content-model-data-hydrator.php
index 0b6dfdd..57fcf92 100644
--- a/includes/runtime/class-content-model-data-hydrator.php
+++ b/includes/runtime/class-content-model-data-hydrator.php
@@ -43,7 +43,7 @@ class Content_Model_Data_Hydrator {
     *
     * @return array The template blocks, filled with data.
     */
-   public function hydrate() {
+   public function hydrate_groups() {
        return content_model_block_walker(
            $this->blocks,
            array( $this, 'hydrate_block' )
diff --git a/includes/runtime/class-content-model.php b/includes/runtime/class-content-model.php
index c66d75b..c38e177 100644
--- a/includes/runtime/class-content-model.php
+++ b/includes/runtime/class-content-model.php
@@ -97,7 +97,7 @@ final class Content_Model {
         *
         * The Editor reads the whole post, while the front-end reads only the post content.
         */
-       add_action( 'the_post', array( $this, 'hydrate_bound_groups' ) );
+       add_action( 'the_post', array( $this, 'inject_and_hydrate_template' ) );
        add_filter( 'the_content', array( $this, 'swap_post_content_with_hydrated_template' ) );

        add_filter( 'get_post_metadata', array( $this, 'cast_meta_field_types' ), 10, 3 );
@@ -483,41 +483,38 @@ final class Content_Model {
    }

    /**
-    * In the editor, display the template and fill bound Groups with data.
+    * Hydrate the template with data.
     * Blocks using the supported Bindings API attributes will be filled automatically.
     *
     * @param WP_Post $post The current post.
     */
-   public function hydrate_bound_groups( $post ) {
+   public function inject_and_hydrate_template( $post ) {
        if ( $this->slug !== $post->post_type ) {
            return;
        }

        $editor_blocks = $this->template;
-       $editor_blocks = ( new Content_Model_Data_Hydrator( $editor_blocks, false ) )->hydrate();
-       $editor_blocks = content_model_block_walker( $editor_blocks, array( $this, 'add_fallback_value_placeholder' ) );

-       $post->post_content = serialize_blocks( $editor_blocks );
-   }
+       $editor_blocks = ( new Content_Model_Data_Hydrator( $editor_blocks, false ) )->hydrate_groups();

-   /**
-    * If a block has bindings modify the placeholder text.
-    *
-    * @param array $block The original block.
-    *
-    * @return array The modified block.
-    */
-   public function add_fallback_value_placeholder( $block ) {
-       $tentative_block = new Content_Model_Block( $block );
+       $editor_blocks = content_model_block_walker(
+           $editor_blocks,
+           function ( $block ) {
+               $tentative_block = new Content_Model_Block( $block );

-       if ( ! empty( $tentative_block->get_bindings() ) ) {
-           // translators: %s is the block variation name.
-           $block['attrs']['placeholder'] = sprintf( __( 'Enter a value for %s' ), $tentative_block->get_block_variation_name() );
+               // If a block has bindings modify the placeholder text.
+               if ( ! empty( $tentative_block->get_bindings() ) ) {
+                   // translators: %s is the block variation name.
+                   $block['attrs']['placeholder'] = sprintf( __( 'Enter a value for %s' ), $tentative_block->get_block_variation_name() );
+               }

-           return $block;
-       }
+               // TODO: lock blocks here.

-       return $block;
+               return $block;
+           }
+       );
+
+       $post->post_content = serialize_blocks( $editor_blocks );
    }

    /**
bacoords commented 2 months ago

I'm not sure this is feasible. Currently we're using setBlockEditingMode which is something that changes the state in the block editor, but not an actual attribute on the block that we can pass server-side (as far as I'm aware). Might need to be addressed upstream in GB?

zaguiini commented 2 months ago

Isn't it the same as setting contentOnly as a block attribute, @bacoords?

bacoords commented 2 months ago

No it's more of an editor state, but they're reworking it now. This comment gives a lot of context of where it's going: https://github.com/WordPress/gutenberg/issues/60021#issuecomment-2346426910