-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Allow block to be constrained to only be used in root (when it has no parent) #7845
Comments
This definitely sounds like a use case we should support. I have a few ideas on how to enable it. 1) Using parentMy first thought is that we should support this via registerBlockType( 'amp-story', {
parent: [ 'post', 'page' ],
} );
registerBlockType( 'amp-story-page', {
parent: [ 'amp-story' ],
} );
registerBlockType( 'amp-story-grid', {
parent: [ 'amp-story-page' ],
} ); We've gone back-and-forth on whether or not to include post types in 2) Using InnerBlocksThis came up in #5540 and #6753 as well. The basic idea is to allow you to specify a blacklist of blocks that cannot go inside a registerBlockType( 'amp-story', {
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} );
registerBlockType( 'amp-story-page', {
parent: [ 'amp-story' ],
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} );
registerBlockType( 'amp-story-grid', {
parent: [ 'amp-story-page' ],
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} ); It's not clear what would happen if one specified both We can, alternatively, combine Thoughts? cc. @WordPress/gutenberg-core |
The use of |
I'm looking for something similar. I want to restrict the insertion of all block but one at the root level. So you can only insert one type of block at the root level. The rest can be inserted inside this block. |
+1 The |
+1 I experince most clients insisting to the classical editor and those few changing to GB tend to screw up their own websites because they are overwhelmed and even the UX is not intuitive enough. Those layouts have blocks but those blocks do not have content types (as they are all just graphical) but the designers give them aliases (like team member that contains a thumbnail and text for example). I mean most probably i do not want to make such a restriction that the client may not place an image block inside a text block... but for sure i do not want to allow them to place any text block in a block that has the alias "portrait gallery". This way we would have an abstraction layer allowing us to group the very same block types together -but as they may have different aliases- we could set up different rules what content they are intended to contain based on the layout we got from the designer. |
I couldn't make Also, this one couldn't work with me:
I would like |
If it helps, here is another use case: custom "slides" post type with "slide" blocks. No other block should be inserted at the root. |
@swissspidy Is this the issue we discussed at WCEU? :) |
@ellatrix Yes I believe so! |
Having a similar need to what a few have described: Limit root-level blocks to one type. Allow multiple blocks within that block. The post type template_locks or allowed_block_types filter (root level) override the individual block templateLock and allowedBlocks. Is it a possibility that block level templateLock and allowedBlocks can override these root-level settings (for their level), or does it need to stay that way by design? Considering the example, child block settings would override parent blocks for their level, and the next child would for theirs, and so on. It seems like there is already a WP precedence for children to override parents. There's many use cases where users should have the flexibility of multiple blocks, but certain layout parts/levels are locked/limited (event/product/realty listings). |
I face the same problem. For now I just hide unnecesary blocks depending where inserter is in DOM structure, but it's neither clean nor working 100%. If only we could set 'root' blocks. Have anyone managed to solve the problem? |
+1 We need this as well, but with the added constraint of only allowing 2 blocks for a specific page template in the root. For example, we have a landing-page template and a home-page template, and on those two templates, we need the only option for editors to be inserting a section block in the root. That section block in turn would allow for about 10 or so blocks in itself. Would be super helpful for us since Gutenberg is inherently a little confusing to a lot of our editors (most of them aren't WordPress editors by trade, its just part of their job to update their sites) and even more confusing is that there are different rules for different page templates (sections can go in these two templates but not these. Your page looks bad here because you picked the landing-page template but didn't use a section...etc.) Really would save our support team a lot of hours of explaining that over and over again if we could just automate which blocks are available given certain conditions. |
+1 The need for block restriction is very real. Is it possible to de-couple the definition modules made in allowedBlocks prop (js) and the 'allowed_block_types' filter (php) so that the InnerBlocks module can grant provision of the allowedBlocks independently? This would provide a way of explicity defining sets of allowed blocks in both the Root and the InnerBlock. |
The next step here is for a developer who's interested in helping out to explore one of the approaches (probably (1)) described in #7845 (comment). I've added the |
Hello, I have registered a custom block as parent to all the default Gutenberg blocks. Also, inside the My question:
My code
` |
It would seem to me that limiting by post type on parent could be limiting. Just specifying I would also suggest this as a "Why not both" scenario. It makes sense to add +1 to dissallowedBlocks |
so... still no way to prevent editors to add blocks in root level, besides the ones we supply in the template?
|
Can we make the definition of I think that adding "root" here is strange because root is not a "parent block", nor is "post" or "page" a parent block. Instead, there is "no" parent block. I think an empty array for For the same reason, I disagree with adding "post type" to the parent definition. I think if we want to add post type restrictions to the block definition it should be as its own property const Tabs = {
// only allow this block in root, don't need tabbed columns
parent: [], // or [ '' ]
}
const Accordion = {
// this can be in a root, a column, a tab
parent: ['', 'core/columns', 'myplugin/tabs'],
} Another complementary idea which expresses the opposite of this would be to set the parent property to If we can agree this is a good way forward, I'll try and take a look and open a PR. |
Here's a workaround that could work:
Not the cleanest approach to nest the whole content in another div to mimic the root, but could work at least. I didn't test this yet, but decided to write this idea down, since perhaps it could be useful for someone looking for a quick hack that makes this feature working now. |
@krywa5 can you show an example of how you do it? |
Hello, Is there any update on this? Can I edit the default column block to updates its disallowed blocks? I don't want the custom blocks to be in the inserter of the column block. |
It is not possible anymore. After gutenberg update a few months ago, the inserter isn't nested inside the block HTML. For now I just dropped the idea. |
Hey so here's how we're currently constraining blocks to certain page templates. The same can probably be used to constrain blocks to just the root document. I have a public repo with this code but I'm newer to open source and last time I tried to link to something on stack overflow I got points deducted...so here's the code and sorry for the length 😬 const { pick, intersection } = window.lodash
const { data, i18n } = window.wp
const { __ } = i18n
const { select, subscribe, dispatch } = data
const { isTyping, getBlocks } = select('core/block-editor')
const { getEditedPostAttribute } = select('core/editor')
const { isEditorPanelOpened, getActiveGeneralSidebarName } = select('core/edit-post')
const { getBlockType } = select('core/blocks')
const { addBlockTypes, removeBlockTypes } = dispatch('core/blocks')
const { updateEditorSettings } = dispatch('core/editor')
/**
* Register a state subscriber ONLY WHEN the predicate is met, then unsubscribe it immediately.
*
* @param {function} predicate
* @param {function} callback
*
* @return {function} The generated unsubscribe function, just in case you need it.
*/
const subscribeOnceWhen = (predicate, callback) => {
const unsubscribe = subscribe(() => {
if (predicate()) {
unsubscribe()
callback()
}
})
return unsubscribe
}
/**
* Map block names to the actual block object.
*
* @type {object}
*/
const unregisteredBlocks = {}
/**
* Defines the map of block types and the templates they are restricted to.
*
* @type {object}
*/
const blockTemplateRestrictions = {
'ufhealth-apollo/hero': [
'template-homepage.php',
],
'ufhealth-apollo/landing-hero': [
'template-landing.php',
],
'ufhealth-apollo/section': [
'template-homepage.php',
'template-landing.php',
],
'ufhealth-apollo/section-heading': [
'template-homepage.php',
'template-landing.php',
],
}
/**
* Currently selected template.
* @type {string}
*/
let currentTemplate = ''
/**
* Current restricted block count.
*
* @type {number}
*/
let currentRestrictedBlocks = 0
/**
* Page Templates loaded with the page.
*
* @type {object}
*/
let defaultPageTemplates = {}
/**
* Are we currently restricting page templates? Determines
* if we show the notice under the select box or not.
*
* @type {boolean}
*/
let isRestricted = false
const blocksLoaded = () => {
const blockList = getBlocks()
return blockList.length > 0
}
/**
* Our whitelist notice that we inject under the Page Templates select box.
*/
const templateWhitelistNotice = document.createElement('p')
const templateWhitelistBlockList = document.createElement('ul')
templateWhitelistNotice.classList.add('components-base-control__note')
templateWhitelistBlockList.classList.add('components-base-control__note')
templateWhitelistNotice.innerText = __('Some page templates are currently unavailable because they are incompatible with the following blocks on this page. You will need to remove them in order to make those templates available again.', 'ufhealth-apollo')
/**
* Add a note below the page template dropdown to inform the user of active template restrictions.
*/
const addTemplateWhitelistNotice = restrictedBlocks => setTimeout(() => {
const wrapper = document.querySelector('.editor-page-attributes__template')
templateWhitelistBlockList.innerHTML = ''
if (restrictedBlocks.length) {
restrictedBlocks.forEach(blockName => {
const block = getBlockType(blockName)
const item = document.createElement('li')
item.innerText = `${block.title} Block`
templateWhitelistBlockList.appendChild(item)
})
}
if (wrapper && wrapper.lastElementChild !== templateWhitelistNotice) {
wrapper.appendChild(templateWhitelistNotice)
wrapper.appendChild(templateWhitelistBlockList)
}
}, 50)
/**
* Removes the notice from under the Page Templates select box.
*/
const removeTemplateWhitelistNotice = () => setTimeout(() => {
const wrapper = document.querySelector('.editor-page-attributes__template')
if (wrapper && wrapper.contains(templateWhitelistNotice)) {
templateWhitelistNotice.remove()
}
}, 50)
/**
* Run our listeners once the editor is finished loading.
*/
const run = () => {
return new Promise(resolve => {
subscribeOnceWhen(blocksLoaded, () => {
defaultPageTemplates = select('core/editor').getEditorSettings().availableTemplates
restrictBlocks()
whitelistPageTemplates()
resolve()
})
})
}
/**
* Responsible for unregistering blocks based on the template selected. It will re-register those
* blocks if a non-restricted template is selected.
*/
const restrictBlocks = () => {
currentTemplate = getEditedPostAttribute('template') || 'default'
restrictBlocksForTemplate(currentTemplate)
subscribe(() => {
if (isTyping()) {
return false
}
const newTemplate = getEditedPostAttribute('template') || 'default'
if (currentTemplate !== newTemplate) {
currentTemplate = newTemplate
restrictBlocksForTemplate(currentTemplate)
}
})
}
/**
* Responsible for hiding templates based off if there exists restricted blocks
* in the editor.
*/
const whitelistPageTemplates = () => {
const blocks = getBlocks()
const { templates, restrictedBlocks } = checkForRestrictedBlocks(blocks)
currentRestrictedBlocks = restrictedBlocks.length
updateWhitelistedTemplates(templates)
addOrRemoveWhitelistNotice(restrictedBlocks)
subscribe(() => {
if (isTyping() === true) {
return false
}
const blocks = getBlocks()
const { templates, restrictedBlocks } = checkForRestrictedBlocks(blocks)
addOrRemoveWhitelistNotice(restrictedBlocks)
if (restrictedBlocks.length !== currentRestrictedBlocks.length) {
currentRestrictedBlocks = restrictedBlocks
updateWhitelistedTemplates(templates, restrictedBlocks)
}
})
}
/**
* Adds the whitelist notice under the Page Templates select box to let
* users know why they only see certain templates.
*/
const addOrRemoveWhitelistNotice = (restrictedBlocks) => {
if (isEditorPanelOpened('page-attributes') && getActiveGeneralSidebarName() === 'edit-post/document') {
if (isRestricted) {
addTemplateWhitelistNotice(restrictedBlocks)
} else {
removeTemplateWhitelistNotice()
}
}
}
/**
* Either registers or unregisters blocks based on the selected template.
*
* @param {string} template
*/
const restrictBlocksForTemplate = (template) => {
const { blocksToRegister, blocksToUnregister } = templateBlockRegistry(template)
if (blocksToUnregister.length) {
blocksToUnregister.forEach((blockName) => {
const blockExists = typeof getBlockType(blockName) !== 'undefined'
const isRegistered = typeof unregisteredBlocks[blockName] === 'undefined'
if (blockExists && isRegistered) {
unregisteredBlocks[blockName] = getBlockType(blockName)
}
})
removeBlockTypes(Object.keys(unregisteredBlocks))
}
if (blocksToRegister.length) {
let registeredBlocks = []
blocksToRegister.forEach(blockName => {
const blockDoesNotExist = typeof getBlockType(blockName) === 'undefined'
const isUnregistered = typeof unregisteredBlocks[blockName] !== 'undefined'
if (blockDoesNotExist && isUnregistered) {
registeredBlocks.push(unregisteredBlocks[blockName])
delete unregisteredBlocks[blockName]
}
})
addBlockTypes(registeredBlocks)
}
}
/**
* Decides what blocks we need to register and unregister
*
* @param {string} template
* @returns {{blocksToRegister: [], blocksToUnregister: []}}
*/
const templateBlockRegistry = (template) => {
let blocksToRegister = []
let blocksToUnregister = []
Object.keys(blockTemplateRestrictions).forEach((block) => {
if (blockTemplateRestrictions[block].includes(template)) {
blocksToRegister.push(block)
} else {
blocksToUnregister.push(block)
}
})
return {
blocksToRegister,
blocksToUnregister
}
}
/**
* Update the editor settings in gutenberg to show only templates that are not restricted
* for certain blocks.
* @param templates
*/
const updateWhitelistedTemplates = (templates) => {
if (templates.length > 0) {
isRestricted = true
updateEditorSettings({ availableTemplates: pick(defaultPageTemplates, templates) })
} else {
isRestricted = false
updateEditorSettings({ availableTemplates: defaultPageTemplates })
}
}
/**
* Checks to see what templates we should restrict to based on the blocks found in the editor.
* If empty, then no templates will be restricted.
*
* @param {array} blocks
* @returns {array}
*/
const checkForRestrictedBlocks = (blocks) => {
let foundTemplates = []
let foundBlocks = []
blocks.forEach(block => {
if (typeof blockTemplateRestrictions[block.name] !== 'undefined') {
foundTemplates.push(blockTemplateRestrictions[block.name])
if (!foundBlocks.includes(block.name)) {
foundBlocks.push(block.name)
}
}
if (block.innerBlocks.length > 0) {
const { templates, restrictedBlocks } = checkForRestrictedBlocks(block.innerBlocks)
if (templates.length > 0) {
foundTemplates.push(templates)
}
restrictedBlocks.forEach(blockName => {
if (!foundBlocks.includes(blockName)) {
foundBlocks.push(blockName)
}
})
}
})
return {
templates: intersection(...foundTemplates),
restrictedBlocks: foundBlocks
}
}
export const RestrictBlocks = { run } Then in your entry file you would just use it like this import { RestrictBlocks } from './path/to/above/snippet'
window.wp.domReady(() => {
RestrictBlocks.run()
}) |
I was able to limit a block to the content (root) level by using It works in my case but would this be sufficient? would this have any edge cases? Note: Using |
Nice, I don't think post-content was a block when this issue was created. Seems like that would have come from the recent Gutenberg layout editor updates. If that's now a block that can be specified as parent, I think this issue can be closed? (I haven't confirmed) |
I know it's not super related, but is the parent info also available in the render_block PHP hook? I want to add some block wrapper html to all/most blocks (also core), but only to the top-level blocks. |
Plus bloody 10 for this - didn't hear about that one sneak in. Also +10 for Anyway, one method I've tried is using select > Unless anyone knows of a method to edit the insertable items or |
I don’t know what all this means. But y if any you
…On Fri, Jan 14, 2565 BE at 7:46 AM Bysander ***@***.***> wrote:
I was able to limit a block to the content (root) level by using parent:
['core/post-content'].
Plus bloody 10 for this - didn't hear about that one sneak in.
Also +10 for disallowedBlocks please - I've needed it for nearly every
nested block I've written
Anyway, one method I've tried is using select > getInserterItems(
clientId ) then removing your "disallowedBlocks" works in the console -
but you can't pass this to allowedBlocks without a clientId - and you
don't have on on the block init.
Unless anyone knows of a method to edit the insertable items or
allowedBlocks after the block has loaded?
—
Reply to this email directly, view it on GitHub
<#7845 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AU6ET4SLZVZDQZUG2CQRBXLUWBHNNANCNFSM4FJB36IA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
I’m afraid to open any other messages or anything on my phone for that
matter. Not sure what I can do or who to bring this to
…On Fri, Jan 14, 2565 BE at 7:46 AM Bysander ***@***.***> wrote:
I was able to limit a block to the content (root) level by using parent:
['core/post-content'].
Plus bloody 10 for this - didn't hear about that one sneak in.
Also +10 for disallowedBlocks please - I've needed it for nearly every
nested block I've written
Anyway, one method I've tried is using select > getInserterItems(
clientId ) then removing your "disallowedBlocks" works in the console -
but you can't pass this to allowedBlocks without a clientId - and you
don't have on on the block init.
Unless anyone knows of a method to edit the insertable items or
allowedBlocks after the block has loaded?
—
Reply to this email directly, view it on GitHub
<#7845 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AU6ET4SLZVZDQZUG2CQRBXLUWBHNNANCNFSM4FJB36IA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Someone has all my info . I have identified theft it’s been noted since at
least 2017. But I think it much closer to hone than I had originally
thought. Except it has completely destroyed my life this time
…On Fri, Jan 14, 2565 BE at 7:46 AM Bysander ***@***.***> wrote:
I was able to limit a block to the content (root) level by using parent:
['core/post-content'].
Plus bloody 10 for this - didn't hear about that one sneak in.
Also +10 for disallowedBlocks please - I've needed it for nearly every
nested block I've written
Anyway, one method I've tried is using select > getInserterItems(
clientId ) then removing your "disallowedBlocks" works in the console -
but you can't pass this to allowedBlocks without a clientId - and you
don't have on on the block init.
Unless anyone knows of a method to edit the insertable items or
allowedBlocks after the block has loaded?
—
Reply to this email directly, view it on GitHub
<#7845 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AU6ET4SLZVZDQZUG2CQRBXLUWBHNNANCNFSM4FJB36IA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
This is a GitHub thread for Wordpress Gutenberg @Rockstar907 is your username and you're posting to a bug board via email. We can't see your details or info. |
Thnx @prathamesh-gharat saved me some time! For me it seems also related to this issue: The Gutenberg editor is really nice, but it still misses some solid functionality. When you build a custom theme where you want to control the editing experience, offer only the blocks/options that are relevant.
|
It's great, even if currently undocumented, that this works with |
Indeed, |
@westonruter as this issue was created 4 years ago, could you please verify that according to @mtias comment here (and previous ones) you're currently able to restrict blocks to only be used from the root of your Custom Post Type? |
@juanmaguitar It's been so long that the original reason I filed this issue is now obsolete. I'll trust what @mtias says 😄 |
Think I found a bug with this solution: When you restrict a block with parent set as |
I've been looking for a way to force a block to only be used at the root level. I'm trying to create a block taxonomy where only one block type (A) is allowed in the root, and it only allows another block type (B) as its child, and then B allows a restricted set of blocks, but not A or B, for example:
A > B > !(A|B)
The context here is implementing AMP Stories in Gutenberg. The structure of an AMP Story is as follows:
So I have an
amp-story
post type which should only take 1 or moreamp-story-page
blocks, and theamp-story-page
blocks should only allow one or moreamp-story-grid-layer
blocks, and then lastlyamp-story-grid-layer
allows a subset of the core blocks (e.g.paragraph
,video
,image
) but not anyamp-story-*
blocks.At the moment, the
amp-story-page
block is offered in the inserter to be added to the root and to any of the other nesting blocks because there is nothing constraining it otherwise.I tried a top-down and bottom-up approach where I explicitly set the
allowedBlocks
and theparent
block property but thus resulted in some infinite recursion error.I also tried to register a block with
parent: []
but this just results in the inserter being hidden entirely, since all of the blocks other thanamp-story-page
have aparent
set to beamp-story-page
oramp-story-grid-layer
.Discussed in Slack at https://wordpress.slack.com/archives/C02QB2JS7/p1531158200000319
/cc @noisysocks
The text was updated successfully, but these errors were encountered: