Last active
April 24, 2020 18:52
-
-
Save myleshyson/d9e07022417e445b555afd90307ba4a7 to your computer and use it in GitHub Desktop.
Restricting blocks to templates in gutenberg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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