Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More versatile templateLock #8112

Open
chrisvanpatten opened this issue Jul 21, 2018 · 26 comments
Open

More versatile templateLock #8112

chrisvanpatten opened this issue Jul 21, 2018 · 26 comments
Labels
[Feature] Nested / Inner Blocks Anything related to the experience of nested/inner blocks inside a larger container, like Group or P [Type] Enhancement A suggestion for improvement.
Milestone

Comments

@chrisvanpatten
Copy link
Member

chrisvanpatten commented Jul 21, 2018

templateLock is a property of InnerBlocks that allows you to "lock" an InnerBlocks area.

As implemented, it is somewhat limited, with only three options: prevent all modifications, prevent inserting, and disable locking (for nested contexts).

I'd like to see a slightly more flexible approach, which reflects different needs in various nesting situations. In particular, templateLock doesn't currently have an option to disable inserting/removing while preserving movement, which I think could be a fairly common use-case (and what prompted me to submit this).

In this more granular approach, you would pass an array of blocked actions:

<InnerBlocks templateLock={['insert', 'move', 'remove']} />
// Could also be aliased as "all"

<InnerBlocks templateLock={['move']} />
// Newly-possible option — allow inserting/removing but no movement

<InnerBlocks templateLock={['insert', 'remove']} />
// Another newly-possible option — _only_ allows moving

<InnerBlocks templateLock={[]} />
// Pass an empty array, or "none", to undo the lock

// You could also continue to pass strings in to preserve back-compat
// (or just make for fewer characters in a user's code)…
<InnerBlocks templateLock="all" />
<InnerBlocks templateLock="none" />
<InnerBlocks templateLock="insert" />
<InnerBlocks templateLock="remove" />

Further, to better reflect that templateLock does not require setting a template (for example if you're building your block structure programatically by dispatching insertBlocks actions), it could be renamed disabledBlockActions (e.g. <InnerBlocks disabledBlockActions={['move', 'remove']} />), or something similar (perhaps disabledBlockUIActions to be absolutely clear to developers that the locks only apply to the UI; you can still programmatically alter the block structure).

I believe this flexibility would be super valuable for anyone doing more complex things with InnerBlocks. To me, InnerBlocks is one of the most exciting features in Gutenberg and I think strengthening this API will have awesome benefits for developers.

(Note: my use-case is most focussed on InnerBlocks, but I suspect these changes could be of use to custom post type templates as well!)

@ZebulanStanphill
Copy link
Member

This is a great idea! I agree that templateLock should be renamed to disabledBlockActions; the meaning of the latter is a lot clearer to me.

@designsimply designsimply added [Type] Enhancement A suggestion for improvement. [Feature] Nested / Inner Blocks Anything related to the experience of nested/inner blocks inside a larger container, like Group or P labels Jul 23, 2018
@chrisvanpatten
Copy link
Member Author

chrisvanpatten commented Jul 25, 2018

Per an earlier conversation with @youknowriad and @jorgefilipecosta on Slack, I wrote up a few use-cases for some of the currently-not-possible functions that this new API would bring.

I focussed on why it would be helpful to split insert and remove in general, because that's currently the main point of confusion in the API. I think move should be treated as a standalone element, and it's a bit easier there to understand why you might want move enabled or disabled separately from insert or remove.

Another important note: I think, to understand the value in this proposed API, it's essential to think about templateLock as entirely separate from templates (it can be used with all <InnerBlocks> areas, whether or not you use a template) and might be programmatically-managed, enabling or disabling functionality based on various conditions (e.g. "in this block list, given X factors, allow Y actions").

Without further blathering on…

Why would you need to disallow insert while allowing remove?

The biggest use-case I see here is a block with a limit to the number of nested children. You could keep track of the children and activate templateLock={['insert']} (or disabledBlockActions) when the limit is reached.

One example might be a "Related Posts" block which allows you to insert a few related posts within a piece of content, but the block creator wants to limit you to up to 3 nested "Post" blocks.

Why would you need to disallow remove while allowing insert?

This could also just be useful in a case that's the reverse of disallowing insert — when you want to enforce a minimum number of blocks, e.g. a custom slideshow block that always needs at least two "slides".

Or this could be a case where you want a block to function as "append-only". One idea I had was for an "Editorial Changelog" block, that allows an editorial team to record their changes to a piece of content over time, but doesn't allow them to remove or re-order past entries.

@chrisvanpatten
Copy link
Member Author

I'm interested in taking a crack at this and just wondering what the API should look like.

Perhaps getTemplateLock could have a method to check if a specific capability exists? E.g.…

getTemplateLock(block.clientId).can('delete')

This would be useful too because it would make it easy to build in back-compat.

Also realising that if this is renamed to something more vague like getAllowedBlockActions, it might also be cool to make it extendable as a way to handle block permissions for specific user roles/blocks. A default set of permissions (allowing all actions) could be filterable, then intersected with the template or InnerBlocks permission set (to prevent devs from allowing specifically blocked permissions, just blocking additional things for specific users).

@aduth
Copy link
Member

aduth commented Aug 15, 2018

@kopepasah
Copy link
Member

@chrisvanpatten I am interested in collaborating with you on this task, as I have an instance where this functionality would prove useful.

Is the expectation that this functionality would also extend to templates? Currently, I have a need to set a default "intro" template for a post, one that cannot be removed or moved, but allows insertion of movable blocks below the template.

Please let me know if/how you want to connect and collaborate on tackling this issue.

@chrisvanpatten
Copy link
Member Author

@kopepasah To be quite honest my schedule is pretty tight at the moment :( I still think there's a lot of merit to this proposal, but I'm not sure I can be the one to tackle it right now, unfortunately. If it's something you think you are up for though, I'd be happy to offer any support I can!

@chrisvanpatten
Copy link
Member Author

Going to put this in the future label. Unlikely to change before 5.0, but I think as InnerBlocks get more powerful in Phase 2 and beyond, it will be necessary.

@mtias mtias added this to the Future: Phase 2 milestone Oct 12, 2018
@youknowriad youknowriad modified the milestone: Future Mar 25, 2019
@janeschindler
Copy link

I'm also interested in this feature. Right now the template lock is not fine-grained enough for me: I need to lock some blocks completely and only allow some blocks in others (which are contained in the completely locked block...). I did notice that if I lock a block template that includes a variable-block template, the input looks correct but after saving all content disappears. Also, if I lock an entire content type template and then add columns, none of the columns are locked. Somewhere – preferably in the same file or admin page – I need to be able to define ALL of the blocks in my template and for each one which blocks are allowed in it. The best example I've seen for setting up this kind of scenario is the ACF flexible layout builder.

@chrisvanpatten
Copy link
Member Author

I'm just revisiting this issue and I still think it merits further discussion and potentially a solution. In general, I think InnerBlocks has some awesome opportunities but some of the mechanics could be improved.

The case of disabling Insert while enabling Remove is especially relevant to me right now, in terms of programmatically enforcing a limit on the number of blocks in an area. I'm still not sure there's a way to accomplish this through other means.

@pbiron
Copy link

pbiron commented May 31, 2019

I support all of what @chrisvanpatten has said about making templateLock more "versatile".

This could also just be useful in a case that's the reverse of disallowing insert — when you want to enforce a minimum number of blocks, e.g. a custom slideshow block that always needs at least two "slides".

However, the slideshow block example would be better handled (IMO) by changes to how block (and CPT) templates are specified. I've been thinking about open an issue about this, but will comment here for now:

I think it would be helpful if block (and CPT) templates were able to specify cardinality (i.e., min/maxOccurs) for the blocks in the template.

@chrisvanpatten
Copy link
Member Author

I keep running into this problem and it's increasingly frustrating. In particular, the fact that an insert lock also prevents removes makes it really hard to do things like dynamically limit the number of blocks in a given area, etc.

However as I think about it, I wonder if — in the interest of backward compatibility — it might make sense to replace templateLock entirely, with a new parameter or parameters?

Something like…

<InnerBlocks
    allowInsert={ false }
    allowMove={ true }
    allowDelete={ false }
/>

These could then be manipulated independently, and ultimately templateLock could be changed under the hood to map to these parameters.

@talldan
Copy link
Contributor

talldan commented Aug 29, 2019

This issue that I'm closing as a duplicate has a similar idea, but suggests even more granular control of block insertion, and being able to restrict insertion to before or after a template only:
#17238

(cc @anUserFr)

@noahtallen
Copy link
Member

This is definitely very important. I'd appreciate some feedback on this comment I left on a different issue which is pretty related: #11681 (comment)

@noahtallen
Copy link
Member

TLDR: since global template validation runs different code than InnerBlocks template validation, it's hard to fix bugs or make enhancements to how block templates work. What if, instead, validation happened at the BlockList level?

@mtias
Copy link
Member

mtias commented Nov 6, 2019

When talking about these restrictions it's fundamental to separate the modification of the interface & interactions from what relates to validating that a block tree matches a specification.

This is important because restricting the interface doesn't really enforce any data structure — you could edit the source and change the order, remove elements, and so on, and InnerBlocks would not know what to do with the result. However, for most practical purposes, restricting the interface is generally enough to define the intended user experience.

This issue, for example, is related to what @jorgefilipecosta is working on about enabling or disabling specific UI elements. It'd be good to model them together because they overlap: #18173

An API of allowInsert or allow[ 'insert' ] could make sense.

It should also be fine to do things like allowing a maximum / minimum number of blocks, and letting InnerBlocks show or hide its UI for insertion based on it.

Separately, I think templateLock should be kept only for template validation issues and move the UI restrictions to more granular controls.

@noahtallen
Copy link
Member

I'm wondering how the two interact. For example, if I have a template lock all on the InnerBlocks, should that influence the allow[ 'insert' ] API? or does the API do its own calculation of what the template says about the current block structure?

@chrisvanpatten
Copy link
Member Author

I 1000% agree with your comment @mtias, and in fact this is how we have moved to address the problem within my organization (admittedly using hacky CSS approaches, rather than a nice declarative API, but the value prop is the same).

Separating the mechanics of template validation, from the available behaviors within the UI, would go a long way toward improving both features.

@janeschindler
Copy link

I'm still wishing there was a clearer and more granular lock/allow functionality. I just created a new issue, which might be a bug (#18711), referring to the fact that what I used to do to solve most of my locking problems – lock the post type but use allowedblocks in a container block – no longer works in wordpress 5.3, at least for me. In any case it would be nice to have a better way of locking or allowing blocks and templates. Thanks for working on this.

@Mahjouba91
Copy link
Contributor

@chrisvanpatten I am interested in collaborating with you on this task, as I have an instance where this functionality would prove useful.

Is the expectation that this functionality would also extend to templates? Currently, I have a need to set a default "intro" template for a post, one that cannot be removed or moved, but allows insertion of movable blocks below the template.

Please let me know if/how you want to connect and collaborate on tackling this issue.

+1 on this, I have the exact same need. It would be awesome to have such control on the editor.

@chrisvanpatten
Copy link
Member Author

There continue to be regular requests for additional flexibility in templateLock.

With the advent of controlled InnerBlocks and some of the implementation changes that are coming to InnerBlocks (thinking of #21368 in particular), I'm curious @noahtallen if you see any new options here for more structured interactions?

@noahtallen
Copy link
Member

I think the main thing that #21368 explores is separating things in the block-editor store which belong to separate entities (or controllers, as we refer to them within the store itself). While it doesn't directly get at the problems we have here ;), I think it is helpful in thinking about how to separate the global block list from different inner blocks which need to be different.

That relates a bit more to #11681. The "block controller" stuff basically makes the code for "sync block-editor blocks with the controlling entity" the same between the top-level entity and a lower-level entity like a template part. I think we want something very similar for templateLock: we want to make template validation consistent between the global template and a local inner blocks template. Currently, template synchronization and validation runs through different code paths when it comes to top-level blocks and inner blocks. I'm not sure fixing that issue directly corresponds to creating a more flexible templateLock mechanism though :)

@chrisvanpatten
Copy link
Member Author

I think we want something very similar for templateLock: we want to make template validation consistent between the global template and a local inner blocks template.

I think this behavior alone makes templateLock more flexible already — and does pave the way for more sophisticated options, even if we don't support them right away!

@emilio-martinez
Copy link
Contributor

Just my two cents: the statement below by @chrisvanpatten in particular rings true in a few use cases for me:

I keep running into this problem and it's increasingly frustrating. In particular, the fact that an insert lock also prevents removes makes it really hard to do things like dynamically limit the number of blocks in a given area, etc.

I haven't mentally itemized all the use cases mentioned in this thread, but what I often bump into is use cases where we may want to control a minimum and a maximum of blocks allowed.

@chrisvanpatten
Copy link
Member Author

This continues to be a pain point in cases where you need more intricate control over the behavior of an InnerBlocks area. There are hacks to get roughly the desired behavior, but they all come with downsides that a native API could eliminate.

@Bysander
Copy link

I've now lost track of the times I've had to make a block less flexible and far more restricted due to lack of a better solution here. Also allowing the conversion of items in the template - ie heading to paragraph within a semi locked down template structure is a common problem I've had to work around.

I did notice that if I lock a block template that includes a variable-block template, the input looks correct but after saving all content disappears.

This also happens regularly when attempting workarounds - such as using dispatch to adapt the innerBlocks in the template

@inaikem
Copy link

inaikem commented May 6, 2021

Adding in another use case:

  • Post template with several custom blocks loaded in at the top of each page.
  • These custom blocks provide post-specific header/meta information to readers.
  • Based on their past use of the Classic editor, authors often grab raw text/code from other posts and drop it into the block editor's code view (links/images, etc.)
  • In some cases, an entire article may be copied wholesale from a Classic block and pasted directly into the code editor.
  • There was no issue when using the Classic editor: Select all, paste over any existing content.
  • However, with the Block editor's code view, authors are regularly (inadvertently) overwriting the custom blocks which appear to them in the code editor as HTML comments.
  • This leads to the pages breaking.

The example above is from active site with several hundred authors producing several thousand articles per week.

Most of the authors have migrated from the Classic editor within the past 12 months, so there is a training component here to be dealt with internally.

However, the ability to lock elements in the code editor (if not both the code and visual editors) would be incredibly useful and represent a meanginful improvement.

Thanks for finding this issue and suggesting I post here, @annezazu 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Nested / Inner Blocks Anything related to the experience of nested/inner blocks inside a larger container, like Group or P [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests