WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.52k stars 4.21k forks source link

What is the proposed way to update blocks? #4849

Closed fabianpimminger closed 6 years ago

fabianpimminger commented 6 years ago

I've been wondering how content should be updated on old posts after the block html-markup changes?

Without using dynamic blocks it is not possible to "re-render"/update all blocks, am I correct? What is the proposed way to update non-dynamic blocks when site owners want to change the markup of the content? Is there any way to do this without opening all old posts and re-saving them? Or should we just use dynamic blocks when markup could change in the future?

youknowriad commented 6 years ago

I'll reply with a question. Do you think it's ok to update old content on a website when a plugin gets updated without the user's consent? Don't you think if the content is published, the user doesn't want it to change unless he does explicitly?

I think most of the time, this is not an issue.

If you'd like to know more about deprecated blocks, see this page https://wordpress.org/gutenberg/handbook/block-api/deprecated-blocks/

And yes, in some small use-cases (remains to be seen for static blocks), you want to update the content in which case, it's probably better to use a dynamic block.

Thanks for opening the issue, I'm closing it right now. Let me know if I didn't answer your concerns.

fabianpimminger commented 6 years ago

Let me give a theoretical example. I'm developing a block for my own blog. I use divs and other current markup to achieve this. Then, a new html element emerges in a future standard and I want to update all instances of this block in all posts because it's a better fit. As I'm the developer of my own block, I know how it will affect my blog.

I think this will drive many developers to choose dynamic blocks and this won't be such a small use-case?

I just wanted to get a confirmation if dynamic blocks are the way to go if I want to have the possibility of updating all instances of one block. Thanks :)

youknowriad commented 6 years ago

Yes, dynamic blocks are the way to go in this case. But your use case is not so common. In general content creators are very different from block authors.

Enjoy Hacking

katerlouis commented 6 years ago

I am facing the same issue and absolutely agree with Fabian on this.

What if the frontend switches to a different carousel plugin, which requires new HTML structure for the galleries. From an editors perspective, the content isn't changing. From a devs perspective, quite a lot has to change.

Or what if you simply made a simple mistake in the blocks output and need to update it?

I mean, there must be a plan to deal with updating blocks, right? I don't even remotely see how updating blocks is an edge case.

When even the slightest changes in my block.js break the block in the editor, I'd really like to have frontend output with PHP hooks back :'(

image

Maybe I'm completely missing something here; please elaborate on what that is, because falling back to RichText or the HTML alone effectively makes complex blocks uneditable once the js code is updated.

wturnerharris commented 6 years ago

So I think we are encountering this very issue, and our only solve is to either have a stable block that will rarely change from a markup-perspective or build deprecations? As an agency we will be constantly iterating over blocks for clients as design needs change or as content structure evolves. To dismiss use-cases is to ignore the community.

I do agree that some way to hook into the output to upgrade a block, to make intelligent decisions on whether to output content based on emptiness would be useful, especially for rapid development and without destroying non-markup content.

There should be some way to manage the data layer. Furthermore, if there is a hook for the "Convert to Blocks" button, then we might be able to refactor on the spot without losing data.

wpmark commented 6 years ago

I also agree that this is a massive problem is the markup of a block cannot be changed without re-saving the posts that use the block. I have tried to add some doc on the dynamic blocks doc page here to explain that using dynamic blocks is the way to go here.

https://github.com/WordPress/gutenberg/pull/8765

However, @tofumatt indicates this is not the way to go - therefore what is?

@youknowriad you mention

Do you think it's ok to update old content on a website when a plugin gets updated without the user's consent?

We are not talking about updating content - just markup.

fabianpimminger commented 6 years ago

@wpmark Good point, because @youknowriad said it is the way to go? I think this should be mentioned in the docs because it is an important factor to decide which type of block developers should use.

youknowriad commented 6 years ago

We are not talking about updating content - just markup.

How is that different from the technical perspective, if we allow people to change markup, they would be able to change content without the user's consent.

There is definitely some improvements we can make to the way we deal with those deprecated versions but dynamic blocks have their drawback:

While I agree that it seems better devX wise, but for the user I'd argue that dynamic blocks are worse and Gutenberg should definitely not emphasize on those. Content blocks are better defined as static blocks for the user.

fabianpimminger commented 5 years ago

I've revisited Gutenberg yesterday (as the 5.0 release is near) and I still think that this is a fundamental problem of Gutenberg.

With the 5.0 release, there are still many blocks that are not 100% ready. The gallery block for example still doesn't solve all issues related to image sizes/responsiveness. As mentioned in #1450, blocks related to image handling will change after 5.0.

So if I update my most important posts after the 5.0 release to include some Gutenberg features (probably 25-50 out of ~100), I will have to manually update all posts again when 5.1 is released to get better support for image handling? And probably again when 5.2 is released?

Contrary to previous Wordpress updates, when image responsiveness (srcset attributes) just got added to ALL posts, this process seems overly cumbersome.

jSanchoDev commented 5 years ago

Correctly me if I'm wrong (b/c I'm new to Gutenberg), but if I develop a plugin with 20 or 30 blocks and release it, then rewrite blocks markup for version 1.0.1, then for 1.0.2, etc. - I'd have to copy/paste the save() / edit() methods code from each previous version? And keep them forever?

If this is the case, then what is the best strategy - to keep the markup always the same? Or to use dynamic blocks for all blocks? What if I have a large markup for block and have to remove some tiny part of it, or correct an error - do I have to keep all this in deprecation history? This may result in huge js bundle size for version 2.0.0 :)

Also, what is the proper way to handle this behavior while developing a block? Each markup change basically invalidates the saved version and you can only convert it to HTML.

davidcrandall99 commented 5 years ago

If the blocks don't update dynamically, it kind of defeats the purpose of using them. I.e, if I change my theme, I may need the blocks to use different html in order to work cohesively with my theme. Otherwise, I'm forced to manually go through all posts and update every block individually.

Marrying the content to the UI is just bad content management. There's no reason that divs, columns, rows, etc., should be part of the SQL markup for the actual content.

Gutenberg has a lot of promise, but it's not a scalable solution for developers.

roryashfordbentley commented 5 years ago

Hypothetically, how would the following be possible:

I have a blog with 1000s of posts. Within most of these posts are multiple Button Blocks that look like this:

<div class="wp-block-button"><a class="wp-block-button__link" href="https://google.com">Link to Google</a></div>

The client has made a support request to add an icon to each button and we would like to change the button markup to the following:

<a class="my-button-class" href="https://google.com">Link to Google<img src="myicon.svg"</a>

Right now, how would it be possible to roll this update out to all existing posts? Would I need to update the Block component and then use something like WP-CLI to do a database find and replace for all existing instances of the button?

fabianpimminger commented 5 years ago

Would I need to update the Block component and then use something like WP-CLI to do a database find and replace for all existing instances of the button?

Yes, … or open each post where the block has been used and save it again using the editor.

(just for the sake of the argument obviously. This could've been done using css for all instances)

roryashfordbentley commented 5 years ago

Yeah this particular example could certainly use CSS I appreciate that :)

I have just been testing out the an ACF (Advanced Custom Fields) acf_register_block() functionality and I really like the way they store and handle the data for blocks.

ACF Gutenberg Blocks are stored in the DB like this:

<!-- wp:acf/button {"id":"block_5c360c7625570","data":{"field_5c3604866626a":"Link to Google","field_5c3604996626b":"https://www.google.com"},"name":"acf/button","align":"","mode":"preview"} /-->

Presumably this would suffer from similar issues with updating old instances of components but I'm a big fan of Key/Value/JSON stores and this way completely seperates markup from content. I can now load a theme component passing it the field values and that component handles all of the presentational styles.

wturnerharris commented 5 years ago

@youknowriad - can this be re-opened? I'm not sure why you closed it. There's a lot of activity on this issue, and it's more widespread than the limited view for the use-cases in the wild.

youknowriad commented 5 years ago

Form the repository management doc https://wordpress.org/gutenberg/handbook/contributors/repository-management/

A healthy issue backlog is one where issues are relevant and actionable. Relevant in the sense that they relate to the project’s current priorities. Actionable in the sense that it’s clear what action(s) need to be taken to resolve the issue.

Any issues that are irrelevant or not actionable should be closed, because they get in the way of making progress on the project. Imagine the issue backlog as a desk: the more clutter you have on it, the more difficult it is to use the space to get work done.

I'd be happy to reopen the issue again but at the moment this is issue is not actionable. I agree that there's a tradeoff here in the way Gutenberg works but sometimes you need to make choices for some users instead of developpers. There's nothing we can do from the Core's side to address the use-cases here at the moment. Do you have any suggestions? actionable items? I'd be happy to reopen and explore if that's the case.

Thanks

katerlouis commented 5 years ago

sometimes you need to make choices for some users instead of developpers

@youknowriad I still don't see how the user benefits from static blocks more than he suffers from them. It's not only in the developers interest to update block-instances through thousands of posts automatically.

For what I intend to do with blocks, there is no question I go for dynamic blocks all the time. I fully agree that the representation/interpretation of data has no place in the database.

leph83 commented 5 years ago

I really want to develop my own block. And of course I will have to make changes to those blocks after they are used on several pages. I've tried the deprecated blocks, to avoid breaking my old versioned blocks completely. But having to save all pages with the used blocks to see my changes is a no go. There's got to be a better solution to that.

braco commented 5 years ago

@youknowriad

I'd be happy to reopen the issue again but at the moment this is issue is not actionable. I agree that there's a tradeoff here in the way Gutenberg works but sometimes you need to make choices for some users instead of developpers. There's nothing we can do from the Core's side to address the use-cases here at the moment. Do you have any suggestions? actionable items? I'd be happy to reopen and explore if that's the case.

There are many Github issues raising the same concern. Forcing deprecation for markup changes as simple as adding another CSS class is totally insane. Gutenberg is currently crippled as a corporate CMS because of this – deprecation is not a reasonable solution for sites with a huge amount of content that need even MINOR layout changes. Even the upgrade path to the new block is broken, it's just such an infuriating overwrought mess that I would strongly dissuade orgs from adopting Wordpress at this point.

As far as protecting users, there could, at the very least, be some kind of Wordpress setting that allowed for automatic upgrading of blocks. You can also assume that some users will be in a controlled environment where they shouldn't be making decisions about what to upgrade. I'm curious how many of your largest, paying customers fall under that umbrella.

edit: there are entire companies built around the premise of having structured input with dynamic output, like Contentful, and Wordpress is frustratingly close to being able to fulfill this goal, but some of the decisions you guys have made are crazy making. There are so many arguments for types of content being attribute-first instead of layout-first that it's hard to even understand how this decision was reached.

youknowriad commented 5 years ago

There's still interest in improving the deprecation/validation/upgrade experience. There's no clear path forward at the moment, if you have proposals please share them in #7604

leph83 commented 5 years ago

How are other developers updating their blocks? I mean there has to be a way since there are so many plugins out there.

codewithfeeling commented 5 years ago

I am amazed that this is such a problem. Seems like complete insanity that if you need to make any kind of a change to the HTML output, your blocks just fall apart. Maddening.

MonsterKing commented 5 years ago

Depending on your situation, there are ways (though they may not be very elegant) to update blocks without using deprecation. For instance, I built a cover block for a client. After it was complete and in use, they wanted the option of having the entire block be "clickable." I added the necessary attributes and changes to the edit section. In the "save" section, I simply added a condition (if a URL has been entered, return html set 1; if not, return the original html). By saving the portion of the html that would be repeated in each condition as a const, I don't have to retype that. Now the same client has asked to have an optional overlay added. Since adding/changing a class after-the-fact causes a rendering error, I can apply the same/similar solution. It make be cringe-worthy to some, but it's really only a few extra lines of code and no pain of breakage or deprecation.

studionenontwerp commented 5 years ago

Just don't use the 'save-section'. I am using Gutenberg just for display in admin and to set the attributes. For display on front-end I am using the wp-filter 'render_block'. A bit like 'render_callback' but this is a more user friendly way of editing a post. Inside 'render_block' I'm creating the html and catch the content of the 'innerBlocks' (all wrapped in my custom class).

add_action( 'render_block' , array( $this ,'render_block_html') , 10 , 2 );

inside 'render_block_html' I am checking for my blockName and call a function by blockName and suffix.

function render_block_html( $content , $block )
{
        if( strpos($block['blockName'] , 'nen/') === 0 )
        {                        
                $name       = str_replace( 'nen/', 'nen_block_', $block['blockName'] );
                $function   = str_replace( '-' , '_' , $name) . '_front_html' ;

                if( is_callable( $function ) )
                {
                        $attrs      = $this->prepare_attributes( $block );
                        $content    = call_user_func( $function , $content , $attrs , $block );
                }
        }
        return $content;
}

To get all attributes I'm merging the default attributes for my block-type with the attributes set in admin. This is because Gutenberg returns only the edited attributes.

function prepare_attributes( $block )
{
        $obj            = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
        $cur_attrs      = $block['attrs'];
        $return_attrs   = array();

        foreach( $obj->attributes as $attr => $setup )
        {
                if( array_key_exists( $attr , $cur_attrs ) )
                {
                        # cur content
                        $return_attrs[$attr] = $cur_attrs[$attr];
                }
                else
                {
                        # default value
                        $return_attrs[$attr] = ( array_key_exists( 'default' , $setup ) ? $setup['default'] : null ) ;
                }
        }

        $return_attrs = apply_filters( 'nen_block_'.$block['blockName'].'_front_html_attributes' , $return_attrs , $block );

        return (object)$return_attrs;
}

So for any custom block I only need to create a new function like 'nenblock' . $name . '_front_html' to output the html. Just dont forget to add the innerBlocks (it's children).

function nen_block_button_front_html( $innerblocks , $attr , $block )
{
    return '<div class="nen-block-button ' . $attr->attributeName . ' ">' . $innerblocks . '</div>';
}
leph83 commented 5 years ago

Just don't use the 'save-section'. I am using Gutenberg just for display in admin and to set the attributes. For display on front-end I am using the wp-filter 'render_block'. A bit like 'render_callback' but this is a more user friendly way of editing a post. Inside 'render_block' I'm creating the html and catch the content of the 'innerBlocks' (all wrapped in my custom class).

add_action( 'render_block' , array( $this ,'render_block_html') , 10 , 2 );

inside 'render_block_html' I am checking for my blockName and call a function by blockName and suffix.

function render_block_html( $content , $block )
{
        if( strpos($block['blockName'] , 'nen/') === 0 )
        {                        
                $name       = str_replace( 'nen/', 'nen_block_', $block['blockName'] );
                $function   = str_replace( '-' , '_' , $name) . '_front_html' ;

                if( is_callable( $function ) )
                {
                        $attrs      = $this->prepare_attributes( $block );
                        $content    = call_user_func( $function , $content , $attrs , $block );
                }
        }
        return $content;
}

To get all attributes I'm merging the default attributes for my block-type with the attributes set in admin. This is because Gutenberg returns only the edited attributes.

function prepare_attributes( $block )
{
        $obj            = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
        $cur_attrs      = $block['attrs'];
        $return_attrs   = array();

        foreach( $obj->attributes as $attr => $setup )
        {
                if( array_key_exists( $attr , $cur_attrs ) )
                {
                        # cur content
                        $return_attrs[$attr] = $cur_attrs[$attr];
                }
                else
                {
                        # default value
                        $return_attrs[$attr] = ( array_key_exists( 'default' , $setup ) ? $setup['default'] : null ) ;
                }
        }

        $return_attrs = apply_filters( 'nen_block_'.$block['blockName'].'_front_html_attributes' , $return_attrs , $block );

        return (object)$return_attrs;
}

So for any custom block I only need to create a new function like 'nenblock' . $name . '_front_html' to output the html. Just dont forget to add the innerBlocks (it's children).

function nen_block_button_front_html( $innerblocks , $attr , $block )
{
    return '<div class="nen-block-button ' . $attr['attributeName'] . ' ">' . $innerblocks . '</div>';
}

Well this is an interesting approach. Do you have an existing plugin example that you could share?

codewithfeeling commented 5 years ago

@studionenontwerp Nice. Sounds like an approach worth pursuing to me. I'm not sure what you are referring to here:

don't use the 'save-section'

But the rest of the code makes sense. I like how you are only really using blocks as an admin-facing tool to set the attributes. Thanks - I'll try something along those lines.

studionenontwerp commented 5 years ago

When creating a block in js it looks like this. The 'edit:' renders editing/display stuff, the 'save:' renders html for front-end that is saved inside post_content/database.

In my opinion saving styling-html into the database should be avoided if possible. Once saved you can never change it unless each post is edited individualy. I prefer working with custom post types where each post type gets its own front-end layout. Detailed post type information is saved as postmeta using AdvancedCustomField. On Front-end this postmeta is displayed inside its own layout, separated from the main content. Saving this layout inside each post is not my way of building webpages. I want to be able to add or change elements. So the gutenberg option for templates per custom post type (predefined set of blocks) is not what i should use, unless the 'save:' returns null :-)

( function( data, blocks, editor, i18n, element , components ) 
{
        var el                  = element.createElement;
    var __                  = i18n.__;

    blocks.registerBlockType( 'nen/button', 
        {
                title: __( 'Button', 'nen' ),
        icon: 'sos',
        category: 'nen',

                attributes: {
                        myRange:            { type: 'number',   default: null, },
            myCheckbox:         { type: 'boolean',  default: false, },                      
        },

        edit: function( props ) 
                {
                        // set variables
                        var attr            = props.attributes;

                        // set onChange functions
                        function onChangeMyRange( value ) { 
                            props.setAttributes( { myRange: value, } ); }
                        function onChangeMyCheckbox( value ) { 
                            props.setAttributes( { myCheckbox: value, } ); }

                        // building editor (example)
                        return  [
                                // inspector stuff 
                                el( editor.InspectorControls, { key: 'inspector' },
                                    el( components.PanelBody, { className: 'nen-panel', title: 'Title' , initialOpen: true },
                                        el( components.RangeControl, {   
                                                className: 'nen-range',
                                                help: __('Range', 'nen' ),
                                                value: attr.myRange,
                                                onChange: onChangeMyRange, }
                                        ),
                                        el( components.CheckboxControl, {
                                                className: 'nen-checkbox',
                                                label: __('Checkbox','nen'),
                                                checked: attr.myCheckbox,
                                                onChange: onChangeMyCheckbox,
                                            }
                                        ),
                                    ),
                                ),
                                // build editor html
                                el( 'div', { className: 'nen-button', },
                                    el( 'div' , { className: 'nen-button-inner' }, 
                                        el( editor.InnerBlocks, {} ),
                                    ),
                                ),
                        ]
                },

                save: function( props ) 
                {
                        // to build front-end html use wp-filter 'render_block' to create html
                        // best advantage: 
                        //  + no html inside post_content / database
                        //  + editable if needed
                        //  + no gutenberg error and breaking of blocks

                        return null;
                }
        });
}(
        window.wp.data,
        window.wp.blocks,
        window.wp.editor,
        window.wp.i18n,
        window.wp.element,
        window.wp.components,
) );

Note that if your block has innerBlocks (children) you need to save them:

save: function( props ) 
{        
            return el( editor.InnerBlocks.Content , {} ) ;
}
codewithfeeling commented 5 years ago

@studionenontwerp Thanks. So you just meant just not to implement the save method in the React component. Thanks for the clarification. I'm going to take your method for a spin - definitely seems like the best approach for now. Hopefully there will soon be a way to flag that block output can be overwritten, as I would love to be managing the content completely within React.

roryashfordbentley commented 5 years ago

As has been discussed I still firmly believe that we should only be storing data in the database, I recently came across editor.js, a block based editor much like gutenberg but they use json to store block data and it's clear from the example on their site (https://codex.so/editor) just how great their data structure is:

"blocks" : [
        {
            "type" : "paragraph",
            "data" : {
                "text" : "Hey. Meet the new Editor. On this page you can see it in action"
            }
        },
        {
            "type" : "list",
            "data" : {
                "style" : "unordered",
                "items" : [
                    "It is a block-styled editor",
                    "It returns clean data output in JSON",
                    "Designed to be extendable and pluggable with a simple API"
                ]
            }
        }
]

Its simple clean and flexible. It only stores data and not code. It would mean that you could write components to render this data easily and cleanly and if you want to change how a block looks across the site it will take minutes vs hours because you don't need to find and replace every single database instance across the entire wp_posts table.

leph83 commented 5 years ago

@studionenontwerp I've tried to create a block like you described but getting an error for $this here: add_action( 'render_block' , array( $this ,'render_block_html') , 10 , 2 );

Could you post or create a repository with the full plugin, so that I can analyze this? I'm still having troubles to understand how to get this up and running

studionenontwerp commented 5 years ago

I merged it in my theme so it's not a plugin. $this refers to the class thats managing my custom blocks. The 'add_action' has to be in the '__construct()' function. The function should be inside the class.

I'll see what i can do to create a plugin. May this can help:


class NEN_Gutenberg_Blocks_Example
{

        private static $_instance = null;

        /**
         * Singleton instance
         *
         */

        public static function instance() 
        {
                if ( is_null( self::$_instance ) ) {
                        self::$_instance = new self();
                }

                return self::$_instance;
        }

        private function __construct() 
        {            
                add_action( 'render_block' , array( $this ,'render_block_html') , 10 , 2 );
        }

        function render_block_html( $content , $block )
        {
                if( strpos($block['blockName'] , 'nen/') === 0 )
                {                        
                        $name       = str_replace( 'nen/', 'nen_block_', $block['blockName'] );
                        $function   = str_replace( '-' , '_' , $name) . '_front_html' ;

                        if( is_callable( $function ) )
                        {
                                $attrs      = $this->prepare_attributes( $block );
                                $content    = call_user_func( $function , $content , $attrs , $block );
                        }

                        // or use
                        // $content = apply_filters( $function , $content , $attrs , $block );
                }
                return $content;
        }

        function prepare_attributes( $block )
        {
                $obj            = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
                $cur_attrs      = $block['attrs'];
                $return_attrs   = array();

                foreach( $obj->attributes as $attr => $setup )
                {
                        if( array_key_exists( $attr , $cur_attrs ) )
                        {
                                # cur content
                                $return_attrs[$attr] = $cur_attrs[$attr];
                        }
                        else
                        {
                                # default value
                                $return_attrs[$attr] = ( array_key_exists( 'default' , $setup ) ? $setup['default'] : null ) ;
                        }
                }

                $return_attrs = apply_filters( 'nen_block_'.$block['blockName'].'_front_html_attributes' , $return_attrs , $block );

                return (object)$return_attrs;
        }

}
NEN_Gutenberg_Blocks_Example::instance();

function nen_block_button_front_html( $innerblocks , $attr , $block )
{
        return '<div class="nen-block-button ' . $attr->attributeName . ' ">' . $innerblocks . '</div>';
}
jodamo5 commented 5 years ago

This is crazy that the intended functionality is that updated blocks will not update!

Our specific scenario is that we have built custom Gutenberg components for our clients' websites. One of these components includes an image as well as a heading, text and a link. When we originally built the block the image was not linked. But after using this block on a number of pages, we're now adjusting the block to make the image be linked too, in order to improve the user experience. It is ridiculous that we need to open up every page this block has been used on and re-save the page just to get the updated code to apply.

I agree with @braco - "Gutenberg is currently crippled as a corporate CMS because of this".

MonsterKing commented 5 years ago

I'm definitely not a fan of Gutenberg and have most my older clients using page builders instead, which are slow and usually overkill. I had this same situation. In the "save" section, I made a conditional. If there is no link/url, the output is as it always had been. If a link/url exists, the new output (anchor around the image) is served. It worked fine for me.

On Sun, Jun 16, 2019 at 10:43 PM jodamo5 notifications@github.com wrote:

This is crazy that the intended functionality is that updated blocks will not update!

Our specific scenario is that we have built custom Gutenberg components for our clients' websites. One of these components includes an image as well as a heading, text and a link. When we originally built the block the image was not linked. But after using this block on a number of pages, we're now adjusting the block to make the image be linked too, in order to improve the user experience. It is ridiculous that we need to open up every page this block has been used on and re-save the page just to get the updated code to apply.

I agree with @braco https://github.com/braco - "Gutenberg is currently crippled as a corporate CMS because of this".

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/WordPress/gutenberg/issues/4849?email_source=notifications&email_token=AA25PJJPUDAVWPQ2235DYATP24P7FA5CNFSM4EPBAZRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODX2C5XY#issuecomment-502542047, or mute the thread https://github.com/notifications/unsubscribe-auth/AA25PJI6C3MX3Y5TVNI56W3P24P7FANCNFSM4EPBAZRA .

jodamo5 commented 5 years ago

@MonsterKing - sorry I think you misunderstood the example. We're fine with writing the conditioning logic in php for when to insert the URL link. The problem we have is that we originally didn't have this logic in the Gutenberg block. Then we decided to add the logic. That new logic works fine whenever we edit a page and re-save it (this causes Gutenberg to save it with the updated block configuration). However the big problem is that to apply this updated block to existing content, we have to open up every page on which this block has been used and then re-save that page, so that it gets the new updated Gutenberg block code. Without this process the updated Gutenberg block code does not get applied.

Thankfully the site we needed to do this on had less than 20 pages that needed to be updated. But we have a site we're working on shortly that will have over 4000 posts when it is launched. If we update the code for a block on that site, we definitely don't want to have to re-save 4000 posts one at a time in order for them to get the updated block layout!!

MonsterKing commented 5 years ago

I see. So every instance of the block uses the same link? In my case, I had a block that included an image, but the image was not a hyperlink. I needed to change it so that the image was a hyperlink -- in some uses of the block, but not others. By adding a condition to the save section, I was able to avoid breakage on blocks that were already in use. Since, in my case, 1) not all images were going to get links; and 2) all links would be different, I couldn't do a global update anyway. We just needed the ability to add a link if we wanted to and didn't want existing block uses without links to be broken.

Sorry for the misunderstanding!

On Mon, Jun 17, 2019 at 9:53 PM jodamo5 notifications@github.com wrote:

@MonsterKing https://github.com/MonsterKing - sorry I think you misunderstood the example. We're fine with writing the conditioning logic in php for when to insert the URL link. The problem we have is that we originally didn't have this logic in the Gutenberg block. Then we decided to add the logic. That new logic works fine whenever we edit a page and re-save it (this causes Gutenberg to save it with the updated block configuration). However the big problem is that to apply this updated block to existing content, we have to open up every page on which this block has been used and then re-save that page, so that it gets the new updated Gutenberg block code. Without this process the updated Gutenberg block code does not get applied.

Thankfully the site we needed to do this on had less than 20 pages that needed to be updated. But we have a site we're working on shortly that will have over 4000 posts when it is launched. If we update the code for a block on that site, we definitely don't want to have to re-save 4000 posts one at a time in order for them to get the updated block layout!!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/WordPress/gutenberg/issues/4849?email_source=notifications&email_token=AA25PJOHKNFAR7AEVBASUKDP3BS3ZA5CNFSM4EPBAZRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODX5FSXQ#issuecomment-502946142, or mute the thread https://github.com/notifications/unsubscribe-auth/AA25PJIUGR3K7K66LV7CZA3P3BS3ZANCNFSM4EPBAZRA .

jodamo5 commented 5 years ago

Thanks to the comments on this post, and some other posts, I can confirm that dynamic blocks are what should be used if you want updates to blocks to be reflected immediately on your site.

Since I had to read multiple posts to understand what was going on, here is a brief summary of key points that might help others:

Default Guternberg behaviour:

Using Dynamic Blocks Instead

We are going to switch all of the blocks we have created over to dynamic blocks, as we always want to have changes immediately applied across all pages where the block has been used.

maurisrx commented 5 years ago

I am facing the same issue and it's quite frustrating since I update my custom block HTML quite often. And I agree with @jodamo5 that we can use dynamic block for this issue. CMIIW, but I think if we ever update the block attributes, the block verification that will break the old block will still happen, right?

leph83 commented 5 years ago

So I was looking for a tutorial to build a dynamic block and stumbled upon this But after installing the plugin the only block that was not working was the dynamic one. Already wrote an issue and hope there is a fix.

leph83 commented 5 years ago

@jodamo5 do you mind posting a working example? This is my 3rd attempt to build a block where I can add an image, title, subtitle, description and link. On the inspector I want to have a dropdown with different styles. Depending on the style I want to add a different markup. I would love to build my own gutenberg block with es5.

I've got following up and running. I can change color attributes and they are saved correctly. But if you type something in the block, that won't be saved. https://github.com/leph83/dynamic_gutenberg_block

vietvho commented 4 years ago

Thanks to the comments on this post, and some other posts, I can confirm that dynamic blocks are what should be used if you want updates to blocks to be reflected immediately on your site.

Since I had to read multiple posts to understand what was going on, here is a brief summary of key points that might help others:

Default Guternberg behaviour:

  • When creating Gutenberg blocks if you use the "Save" function, which is recommended in nearly all the tutorials, the exact HTML content of your block is saved into the database.
  • If you then update the code of your block at all (even by adding a new class to an element), the changes will not be seen on the front end - Wordpress keeps pulling in the HTML it has saved in the database.
  • When a user edits a page where the block has been updated, they'll see a warning on that block saying it has been externally modified. They need to choose whether to edit it as HTML or convert it to a block again. This is annoying (and concerning!) for users - not a good user experience at all. If they update the block and save the page, only then will the updated block format be seen on the front end of the site.

Using Dynamic Blocks Instead

  • Using dynamic blocks means that when you update the code of your block, the changes will be immediately seen on the front end and there are no verification issues in the editor.
  • To create a dynamic block you need to return null for the save function. This stops Gutenberg saving the HTML into the database. (As explained by @studionenontwerp above).
  • Attributes in your block are automatically saved to the database when the page is saved. (These are stored as HTML comments in the post entry in the database, which Gutenberg reads). So save all content of your block as attributes. In my example we save the Title, the image URL, the link text and the link URL as attributes. This means all these details are saved in the database and can be used when we render the block.
  • We then use render_callback (or @studionenontwerp used render_block) when displaying the block on the front end, so that our block layout code is pulled in dynamically. We can access the attributes and use these as the data when rendering the block on the front end. So the HTML is always built on the fly from our PHP code, which means that any update we make to the block will be immediately replicated across the site.

We are going to switch all of the blocks we have created over to dynamic blocks, as we always want to have changes immediately applied across all pages where the block has been used.

But Serversiderender is not useful in some case: Example: When you build a plugin with many elements and attributes inside. After that, you want to update features that change much more attribute and belong attribute/elements, js action. The ServerSiderender will make your editing Block very slow and can not call new js inside new ServerSiderender. Could we disable the Block Validations between too version update and just get the newest Block saving by JS? Thank you

neryams commented 4 years ago

To anyone looking for a way to get around this, you can tie into the the_post hook and update the post content using your plugin. Detect if a post has an older version of your block output somehow, then update it using string replaces or whever methods you have at your disposal.

This will also work in the admin edit systems - as long as the hook's output matches the output of your new block code, the admin won't complain.

pratham2003 commented 4 years ago

There is also a technical road block to this that no one has spoken of so far in this thread..

The block's structure is defined in a JavaScript file which generates the HTML (client side) when editing an article.

We would need to figure out a way for PHP to update multiple articles when a block's structure is updated in JavaScript. Some kind of PHP/NodeJS based server-side rendering of JavaScript?

Can someone point me to a page that documents reasons for WordPress' decision to use JS for HTML template instead of PHP?

neryams commented 4 years ago

There is also a technical road block to this that no one has spoken of so far in this thread..

The block's structure is defined in a JavaScript file which generates the HTML (client side) when editing an article.

We would need to figure out a way for PHP to update multiple articles when a block's structure is updated in JavaScript. Some kind of PHP/NodeJS based server-side rendering of JavaScript?

Can someone point me to a page that documents reasons for WordPress' decision to use JS for HTML template instead of PHP?

I think the upgrade would be a manual process; i.e. an upgrade script that the author would have to write that would run to update the HTML properly from one version to another, if the output needs to be changed.

ckmahoney commented 4 years ago

There is also a technical road block to this that no one has spoken of so far in this thread..

The block's structure is defined in a JavaScript file which generates the HTML (client side) when editing an article.

We would need to figure out a way for PHP to update multiple articles when a block's structure is updated in JavaScript. Some kind of PHP/NodeJS based server-side rendering of JavaScript?

Can someone point me to a page that documents reasons for WordPress' decision to use JS for HTML template instead of PHP?

@pratham2003 If you want to edit the content of multiple posts at once, you can use some of the ideas in this StackOverflow post.

As far as a reason to use JS over PHP, Gutenberg has explained its intentions pretty thoroughly in the handbook. In short it is about the editing experience. If you want strong UX, you need JavaScript.

I'd support it further by saying that JavaScript is undeniably the immediate future of web development. Through popular frameworks, including React, we are able to easily create stunning UX. PHP does not provide stunning UX.

MonsterKing commented 4 years ago

When my client's (I just have one for whom I've developed nearly 30 blocks, the rest are sticking with builders) custom blocks break, I look at the content in the database to see what's changed. I have written a php script that I access via a page I created for myself in the Dashboard. I click a button and the script looks (in the database) for the text that all broken blocks share, as well as, the names of the classes of the blocks (so I don't accidentally apply my fix to other blocks) and it then fixes the blocks by updating the records in the database directly. I could run it as a cron job, but I prefer to know when things have broken and it's pretty easy to check after a Wordpress update.

On Tue, Mar 10, 2020 at 9:13 AM Cortland Mahoney notifications@github.com wrote:

There is also a technical road block to this that no one has spoken of so far in this thread..

The block's structure is defined in a JavaScript file which generates the HTML (client side) when editing an article.

We would need to figure out a way for PHP to update multiple articles when a block's structure is updated in JavaScript. Some kind of PHP/NodeJS based server-side rendering of JavaScript?

Can someone point me to a page that documents reasons for WordPress' decision to use JS for HTML template instead of PHP?

@pratham2003 https://github.com/pratham2003 If you want to edit the content of multiple posts at once, you can use some of the ideas in this StackOverflow post https://wordpress.stackexchange.com/questions/310301/check-what-gutenberg-blocks-are-in-post-content .

As far as a reason to use JS over PHP, Gutenberg has explained its intentions pretty thoroughly in the handbook https://developer.wordpress.org/block-editor/principles/. In short it is about the editing experience. If you want strong UX, you need JavaScript.

I'd support it further by saying that JavaScript is undeniably the immediate future of web development. Through popular frameworks, including React, we are able to easily create stunning UX. PHP does not provide stunning UX.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/WordPress/gutenberg/issues/4849?email_source=notifications&email_token=AA25PJIOTOINBSZIWNFMAJLRGZRJHA5CNFSM4EPBAZRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEOMCLZA#issuecomment-597173732, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA25PJMMHO7VGNSKXPEDUWLRGZRJHANCNFSM4EPBAZRA .

ddluc commented 4 years ago

Do you think it's ok to update old content on a website when a plugin gets updated without the user's consent? Don't you think if the content is published, the user doesn't want it to change unless he does explicitly?

@youknowriad

Why not just let the user choose? I do agree that it would be bad if the plugin was updated and the markup (or worse, the content) was modified, resulting in unconsented updates on a user's website. But, I think that only applies to publicly released plugins. For me, I'm a known vendor for my clients, and any updates I'd make to a block have explicit constent—or have been requested—to be updated.

What would it take for there to be a setting that the user can set from the block sidebar, which allows static blocks to automatically apply any updates that have been made in the block's save method? I don't have experience contributing to WP, but this is such a headache for me that I'd be willing to devote some time to learning and implementing this feature.

It would be a much much better development experience if the front-end portion of the block could just be built using Javascript/JSX rather than having to revert to PHP just so I can have my edits automatically detected and applied.

marvinpoo commented 4 years ago

Why not just let the user choose? I do agree that it would be bad if the plugin was updated and the markup (or worse, the content) was modified, resulting in unconsented updates on a user's website.

Exactly!

I have no idea how everyone on here works, but I am sure most, if not all of us are following the laws, because as far as I know altering code without the consent of the owner (aka Client) is considered illegal in most western countries anyway, so why use the excuse of "you could update the content without consent" if it would include a illegal action of the dev. Is this the strategy of community management of Wordpress since Gutenberg? It's time to admit there have been some major problems in handling the transition and was forced way to hard and way to fast.

jodamo5 commented 4 years ago

I have no idea how everyone on here works, but I am sure most, if not all of us are following the laws, because as far as I know altering code without the consent of the owner (aka Client) is considered illegal in most western countries anyway, @marvinpoo

I think you've got things confused. If the law worked the way you describe then all plugin updates that have been happening for many years would be illegal! Because plugin updates alter code used on sites. Instead, let's take a step back and look at the main issue being described here:

A developer creates a block. That block is used on hundreds of pages - or maybe even hundreds or thousands of sites. The developer then wants to make some improvements to the block - maybe because users have requested improved functionality, or there is a bug with recent browser updates, or the developer finds a problem that should be fixed. When the update is applied to the site, the fix should be applied immediately to all current instances of the block, so that the issue is fixed everywhere they block is used. However, with the current deprecation process, all of the existing blocks would keep their old buggy state and only new blocks would get the fixed version. Multiply this across hundreds or thousands of pages - that is why it is impractical to get the user to go to every page that the block is used and edit and resave to upgrade to the new block.

Our specific scenario that we personally had to deal with was that one of the custom Gutenberg blocks we built for a client's website included an image, a heading, text and a link. When we originally built the block the image was not linked only the heading was. But after using this block on a number of pages, we then wanted to adjust the block to make the image be linked too, in order to improve the user experience. It is ridiculous that we need to open up every page this block has been used on and re-save the page just to get the updated code to apply. When a client requests a change like this, Gutenberg blocks should all update automatically to reflect the new version of the block - which is exactly what used to happen with PHP blocks in Wordpress. For us to achieve this, we now build all blocks as Dynamic Blocks. But this is not exactly ideal as it has some limitations (e.g. no nested blocks) and isn't the intended use of Gutenberg.

So this is still definitely an area that needs improvement! The conversation around how to improve this is happening on #7604 but there doesn't seem to be much confirmed progress so far.

marvinpoo commented 4 years ago

I think you've got things confused. If the law worked the way you describe then all plugin updates that have been happening for many years would be illegal! Because plugin updates alter code used on sites.

Thats technically something different. When you buy/download a plugin and enable auto updates/ updates, then you give consent to the altering or even agreed to the tos of the distributors shop and their conditions. If the dev changes something on the website without the consent of the client, then they would theoretically do a unauthorized modifiaction. Tho, as with everything in law it is, if no one sues, no one is sued. It's like with domains. If you would just move a domain from A to B without the consent of your client, you have breaken the law, but no (serious & integer) developer would move the domain of a client without their permission, same goes for altering the website/priject in general.

What I was trying to express was, that in the normal behaviour a developer has a contract or atleast the permission to change/edit/differ something in the code. If you as developer change something on a website of a (ex)client without their consent then you are theoretically breaking the law. That doesn't mean in most cases your (ex)client wouldn't be thankful for the change (as example security fix or similar). But no developer would just go to their clients website and edit something out of the blue.

But afterall, that was just a side node pointing to the excuse that this feature is not being aimed on because devs would alter the content, which puts the developer into a position of distrust by a thirdparty bystander. I am sure most developers have contracts or similar agreements with their clients.

Our specific scenario that we personally had to deal with was that one of the custom Gutenberg blocks we built for a client's website included an image, a heading, text and a link. When we originally built the block the image was not linked only the heading was. But after using this block on a number of pages, we then wanted to adjust the block to make the image be linked too, in order to improve the user experience. It is ridiculous that we need to open up every page this block has been used on and re-save the page just to get the updated code to apply. When a client requests a change like this, Gutenberg blocks should all update automatically to reflect the new version of the block - which is exactly what used to happen with PHP blocks in Wordpress. For us to achieve this, we now build all blocks as Dynamic Blocks. But this is not exactly ideal as it has some limitations (e.g. no nested blocks) and isn't the intended use of Gutenberg.

This is so true and basically exactly the same problem we have encountered recently (tho this wasn't the first time this annoyed us). Our example was different but very close. We've used infocards on some sites and wanted to add the ability for the client to choose their own icon at the infocard instead of a simple arrow icon. The old version and the new version are technically the same except for the fact that the content of the

<i class="material-icons">{ attributes.urlicon }</i>

is being altered by the input of it.

I think it is very unpractical for both sides, the developer and the client and needs some work. We have some clients that work on their own content and for some clients we edit the content. If only we would be changing the content, I could live with that, but when I have to tell a client that he has to edit all of his pages because we added a new icon feature, that raises some questions in our clients if we are even competent enough. We understand in the technical way why and how this needs to be done atm, but when I tell that a client who understands the basics of the web, they just plainly ask us, "why did you choose that cms in the first place then?" and honestly, the client is correct. Why would we keep using wordpress if the basics of contenting will always bring up problems like this.

For us to achieve this, we now build all blocks as Dynamic Blocks. But this is not exactly ideal as it has some limitations (e.g. no nested blocks) and isn't the intended use of Gutenberg.

Yes, that is my concern, too. As @youknowriad stated in 2018

There is definitely some improvements we can make to the way we deal with those deprecated versions but dynamic blocks have their drawback: If you disable the plugin, there's no content anymore If for some reason the PHP filter is not executed (think about plugins exploiting the database), the content is there and it's semantically valid.

Thank you for linking to the correct issue, by the way. Let's hope there will be a acceptable solution for this topic at some point. Until then, we will keep the use of blocks at a minimum.

pwkip commented 4 years ago

@youknowriad concerning these quotes:

Do you think it's ok to update old content on a website when a plugin gets updated without the user's consent?

and

How is that different from the technical perspective, if we allow people to change markup, they would be able to change content without the user's consent.

Plugin authors already have the power to do this. Nothing (technical) is preventing them from running a DB query and modifying the code parts between <!-- org:block --> and <!-- /org:block -->

Now they would do it all in their own way, instead of doing it in a well documented manner. Having an approach well documented in one place, could also include a best practices note to encourage plugin developers to actually ask the user for consent before running the actual DB updates.

I am not saying that updating a block should automatically update all existing posts. What I'm (and I believe most people here are) asking is some kind of standardized way or documentation on how to bulk update the blocks in existing posts.

Do you have any suggestions? actionable items? I'd be happy to reopen and explore if that's the case.

Here's a suggestion:

Would this be sufficient to reopen the issue?