Skip to content

Instantly share code, notes, and snippets.

@myleshyson
Last active April 24, 2020 18:52
Show Gist options
  • Save myleshyson/d9e07022417e445b555afd90307ba4a7 to your computer and use it in GitHub Desktop.
Save myleshyson/d9e07022417e445b555afd90307ba4a7 to your computer and use it in GitHub Desktop.
Restricting blocks to templates in gutenberg
const { data } = window.wp
const { select, dispatch, subscribe } = data
const { getEditedPostAttribute } = select('core/editor')
const { isTyping } = select('core/block-editor')
const { getBlockType } = select('core/blocks')
const { addBlockTypes, removeBlockTypes } = dispatch('core/blocks')
class BlockRestrictor {
/**
* Defines the map of block types and the templates they are restricted to.
*
* @type {object}
*/
blockTemplateRestrictions = {}
/**
* Currently selected template.
* @type {string}
*/
currentTemplate = ''
/**
* Map block names to the actual block object.
*
* @type {object}
*/
unregisteredBlocks = {}
constructor(blockTemplateRestrictions) {
this.blockTemplateRestrictions = blockTemplateRestrictions
}
/**
* Initiates listening to the redux store for when a restricted block is either
* added or removed.
*/
run() {
this.currentTemplate = getEditedPostAttribute('template') || 'default'
this.restrictBlocksToTemplate()
subscribe(() => {
if (isTyping() === true) {
return false
}
const newTemplate = getEditedPostAttribute('template') || 'default'
if (this.currentTemplate !== newTemplate) {
this.currentTemplate = newTemplate
this.restrictBlocksToTemplate()
}
})
}
/**
* Either removes or adds blocks to the store based on what the current
* template is.
*/
restrictBlocksToTemplate() {
const { blocksToAdd, blocksToRemove } = this.templateBlockRegistry()
if (blocksToRemove.length) {
blocksToRemove.forEach((blockName) => {
const blockExists = getBlockType(blockName)
const isRegistered = typeof this.unregisteredBlocks[blockName] === 'undefined'
if (blockExists && isRegistered) {
this.unregisteredBlocks[blockName] = getBlockType(blockName)
}
})
removeBlockTypes(Object.keys(this.unregisteredBlocks))
}
if (blocksToAdd.length) {
let registeredBlocks = []
blocksToAdd.forEach(blockName => {
const blockExists = typeof getBlockType(blockName) === 'undefined'
const isUnregistered = typeof this.unregisteredBlocks[blockName] !== 'undefined'
if (blockExists && isUnregistered) {
registeredBlocks.push(this.unregisteredBlocks[blockName])
delete this.unregisteredBlocks[blockName]
}
})
addBlockTypes(registeredBlocks)
}
}
/**
* Helps decide which blocks we actually want to add or remove from
* the store.
*/
templateBlockRegistry() {
let blocksToAdd = []
let blocksToRemove = []
Object.keys(this.blockTemplateRestrictions).forEach((blockName) => {
if (this.blockTemplateRestrictions[blockName].includes(this.currentTemplate)) {
blocksToAdd.push(blockName)
} else {
blocksToRemove.push(blockName)
}
})
return {
blocksToAdd,
blocksToRemove,
}
}
}
export default BlockRestrictor
import BlockRestrictor from './BlockRestrictor'
import TemplateWhitelister from './TemplateWhitelister'
/**
* Defines the map of block types and the templates they are restricted to.
*
* @type {object}
*/
const blockTemplateRestrictions = {
'core/code': [
'template-super-cool.php',
],
}
wp.domReady(() => {
const restrictor = new BlockRestrictor(blockTemplateRestrictions)
const templateWhitelister = new TemplateWhitelister(blockTemplateRestrictions)
restrictor.run()
templateWhitelister.run()
})
import pick from 'lodash/pick'
import intersection from 'lodash/intersection'
const { data} = window.wp
const { select, dispatch, subscribe } = data
const { isTyping, getBlocks } = select('core/block-editor')
const { updateEditorSettings } = dispatch('core/editor')
class TemplateWhitelister {
/**
* Defines the map of block types and the templates they are restricted to.
*
* @type {object}
*/
blockTemplateRestrictions = {}
/**
* Current restricted block count.
*
* @type {number}
*/
currentRestrictedBlocks = 0
/**
* Page Templates loaded with the page.
*
* @type {object}
*/
defaultPageTemplates = {}
constructor(restrictedBlocks) {
this.defaultPageTemplates = select('core/editor').getEditorSettings().availableTemplates
this.blockTemplateRestrictions = restrictedBlocks
}
/**
* Intiates watching the redux store for a template change.
*/
run() {
const blocks = getBlocks()
const { templates, restrictedBlocks } = this.checkForRestrictedBlocks(blocks)
this.currentRestrictedBlocks = restrictedBlocks.length
this.updateWhitelistedTemplates(templates)
subscribe(() => {
if (isTyping() === true) {
return false
}
const blocks = getBlocks()
const { templates, restrictedBlocks } = this.checkForRestrictedBlocks(blocks)
if (restrictedBlocks.length !== this.currentRestrictedBlocks.length) {
this.currentRestrictedBlocks = restrictedBlocks
this.updateWhitelistedTemplates(templates, restrictedBlocks)
}
})
}
/**
* Checks the editor to see if there are any blocks that are restricted to
* specific templates.
*
* @param {array} blocks
* @return {object}
*/
checkForRestrictedBlocks(blocks) {
let foundTemplates = []
let foundBlocks = []
blocks.forEach(block => {
if (typeof this.blockTemplateRestrictions[block.name] !== 'undefined') {
foundTemplates.push(this.blockTemplateRestrictions[block.name])
if (!foundBlocks.includes(block.name)) {
foundBlocks.push(block.name)
}
}
if (block.innerBlocks.length > 0) {
const { templates, restrictedBlocks } = this.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,
}
}
/**
* Updates the availble templates in the editor. Ensures editors can only select
* templates that allow blocks currently being used.
*
* @param {array} templates
*/
updateWhitelistedTemplates(templates) {
if (templates.length > 0) {
updateEditorSettings({ availableTemplates: pick(this.defaultPageTemplates, templates) })
} else {
updateEditorSettings({ availableTemplates: this.defaultPageTemplates })
}
}
}
export default TemplateWhitelister
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment