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!)
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.
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…
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.
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.
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).
Discussed in today's Core Editor chat:
https://wordpress.slack.com/archives/C02QB2JS7/p1534340662000100
@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.
@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!
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.
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.
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.
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.
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.
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:
https://github.com/WordPress/gutenberg/issues/17238
(cc @anUserFr)
This is definitely very important. I'd appreciate some feedback on this comment I left on a different issue which is pretty related: https://github.com/WordPress/gutenberg/issues/11681#issuecomment-549641097
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?
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: https://github.com/WordPress/gutenberg/pull/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.
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?
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.
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 (https://github.com/WordPress/gutenberg/issues/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.
@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.
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?
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 :)
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!
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.
Most helpful comment
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
andremove
in general, because that's currently the main point of confusion in the API. I thinkmove
should be treated as a standalone element, and it's a bit easier there to understand why you might wantmove
enabled or disabled separately frominsert
orremove
.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 allowingremove
?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']}
(ordisabledBlockActions
) 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 allowinginsert
?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.