@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
365 lines (354 loc) • 14.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
setCurrentTemplateId: true,
createTemplate: true,
showBlockTypes: true,
hideBlockTypes: true,
saveDirtyEntities: true,
revertTemplate: true,
removeTemplates: true
};
exports.saveDirtyEntities = exports.revertTemplate = exports.removeTemplates = exports.hideBlockTypes = exports.createTemplate = void 0;
exports.setCurrentTemplateId = setCurrentTemplateId;
exports.showBlockTypes = void 0;
var _coreData = require("@wordpress/core-data");
var _i18n = require("@wordpress/i18n");
var _notices = require("@wordpress/notices");
var _blockEditor = require("@wordpress/block-editor");
var _preferences = require("@wordpress/preferences");
var _url = require("@wordpress/url");
var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch"));
var _blocks = require("@wordpress/blocks");
var _htmlEntities = require("@wordpress/html-entities");
var _isTemplateRevertable = _interopRequireDefault(require("./utils/is-template-revertable"));
var _privateActions = require("../dataviews/store/private-actions");
Object.keys(_privateActions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _privateActions[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _privateActions[key];
}
});
});
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/**
* Returns an action object used to set which template is currently being used/edited.
*
* @param {string} id Template Id.
*
* @return {Object} Action object.
*/
function setCurrentTemplateId(id) {
return {
type: 'SET_CURRENT_TEMPLATE_ID',
id
};
}
/**
* Create a block based template.
*
* @param {Object?} template Template to create and assign.
*/
const createTemplate = template => async ({
select,
dispatch,
registry
}) => {
const savedTemplate = await registry.dispatch(_coreData.store).saveEntityRecord('postType', 'wp_template', template);
registry.dispatch(_coreData.store).editEntityRecord('postType', select.getCurrentPostType(), select.getCurrentPostId(), {
template: savedTemplate.slug
});
registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)("Custom template created. You're in template mode now."), {
type: 'snackbar',
actions: [{
label: (0, _i18n.__)('Go back'),
onClick: () => dispatch.setRenderingMode(select.getEditorSettings().defaultRenderingMode)
}]
});
return savedTemplate;
};
/**
* Update the provided block types to be visible.
*
* @param {string[]} blockNames Names of block types to show.
*/
exports.createTemplate = createTemplate;
const showBlockTypes = blockNames => ({
registry
}) => {
var _registry$select$get;
const existingBlockNames = (_registry$select$get = registry.select(_preferences.store).get('core', 'hiddenBlockTypes')) !== null && _registry$select$get !== void 0 ? _registry$select$get : [];
const newBlockNames = existingBlockNames.filter(type => !(Array.isArray(blockNames) ? blockNames : [blockNames]).includes(type));
registry.dispatch(_preferences.store).set('core', 'hiddenBlockTypes', newBlockNames);
};
/**
* Update the provided block types to be hidden.
*
* @param {string[]} blockNames Names of block types to hide.
*/
exports.showBlockTypes = showBlockTypes;
const hideBlockTypes = blockNames => ({
registry
}) => {
var _registry$select$get2;
const existingBlockNames = (_registry$select$get2 = registry.select(_preferences.store).get('core', 'hiddenBlockTypes')) !== null && _registry$select$get2 !== void 0 ? _registry$select$get2 : [];
const mergedBlockNames = new Set([...existingBlockNames, ...(Array.isArray(blockNames) ? blockNames : [blockNames])]);
registry.dispatch(_preferences.store).set('core', 'hiddenBlockTypes', [...mergedBlockNames]);
};
/**
* Save entity records marked as dirty.
*
* @param {Object} options Options for the action.
* @param {Function} [options.onSave] Callback when saving happens.
* @param {object[]} [options.dirtyEntityRecords] Array of dirty entities.
* @param {object[]} [options.entitiesToSkip] Array of entities to skip saving.
* @param {Function} [options.close] Callback when the actions is called. It should be consolidated with `onSave`.
*/
exports.hideBlockTypes = hideBlockTypes;
const saveDirtyEntities = ({
onSave,
dirtyEntityRecords = [],
entitiesToSkip = [],
close
} = {}) => ({
registry
}) => {
const PUBLISH_ON_SAVE_ENTITIES = [{
kind: 'postType',
name: 'wp_navigation'
}];
const saveNoticeId = 'site-editor-save-success';
const homeUrl = registry.select(_coreData.store).getEntityRecord('root', '__unstableBase')?.home;
registry.dispatch(_notices.store).removeNotice(saveNoticeId);
const entitiesToSave = dirtyEntityRecords.filter(({
kind,
name,
key,
property
}) => {
return !entitiesToSkip.some(elt => elt.kind === kind && elt.name === name && elt.key === key && elt.property === property);
});
close?.(entitiesToSave);
const siteItemsToSave = [];
const pendingSavedRecords = [];
entitiesToSave.forEach(({
kind,
name,
key,
property
}) => {
if ('root' === kind && 'site' === name) {
siteItemsToSave.push(property);
} else {
if (PUBLISH_ON_SAVE_ENTITIES.some(typeToPublish => typeToPublish.kind === kind && typeToPublish.name === name)) {
registry.dispatch(_coreData.store).editEntityRecord(kind, name, key, {
status: 'publish'
});
}
pendingSavedRecords.push(registry.dispatch(_coreData.store).saveEditedEntityRecord(kind, name, key));
}
});
if (siteItemsToSave.length) {
pendingSavedRecords.push(registry.dispatch(_coreData.store).__experimentalSaveSpecifiedEntityEdits('root', 'site', undefined, siteItemsToSave));
}
registry.dispatch(_blockEditor.store).__unstableMarkLastChangeAsPersistent();
Promise.all(pendingSavedRecords).then(values => {
return onSave ? onSave(values) : values;
}).then(values => {
if (values.some(value => typeof value === 'undefined')) {
registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('Saving failed.'));
} else {
registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)('Site updated.'), {
type: 'snackbar',
id: saveNoticeId,
actions: [{
label: (0, _i18n.__)('View site'),
url: homeUrl
}]
});
}
}).catch(error => registry.dispatch(_notices.store).createErrorNotice(`${(0, _i18n.__)('Saving failed.')} ${error}`));
};
/**
* Reverts a template to its original theme-provided file.
*
* @param {Object} template The template to revert.
* @param {Object} [options]
* @param {boolean} [options.allowUndo] Whether to allow the user to undo
* reverting the template. Default true.
*/
exports.saveDirtyEntities = saveDirtyEntities;
const revertTemplate = (template, {
allowUndo = true
} = {}) => async ({
registry
}) => {
const noticeId = 'edit-site-template-reverted';
registry.dispatch(_notices.store).removeNotice(noticeId);
if (!(0, _isTemplateRevertable.default)(template)) {
registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('This template is not revertable.'), {
type: 'snackbar'
});
return;
}
try {
const templateEntityConfig = registry.select(_coreData.store).getEntityConfig('postType', template.type);
if (!templateEntityConfig) {
registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('The editor has encountered an unexpected error. Please reload.'), {
type: 'snackbar'
});
return;
}
const fileTemplatePath = (0, _url.addQueryArgs)(`${templateEntityConfig.baseURL}/${template.id}`, {
context: 'edit',
source: template.origin
});
const fileTemplate = await (0, _apiFetch.default)({
path: fileTemplatePath
});
if (!fileTemplate) {
registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('The editor has encountered an unexpected error. Please reload.'), {
type: 'snackbar'
});
return;
}
const serializeBlocks = ({
blocks: blocksForSerialization = []
}) => (0, _blocks.__unstableSerializeAndClean)(blocksForSerialization);
const edited = registry.select(_coreData.store).getEditedEntityRecord('postType', template.type, template.id);
// We are fixing up the undo level here to make sure we can undo
// the revert in the header toolbar correctly.
registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, template.id, {
content: serializeBlocks,
// Required to make the `undo` behave correctly.
blocks: edited.blocks,
// Required to revert the blocks in the editor.
source: 'custom' // required to avoid turning the editor into a dirty state
}, {
undoIgnore: true // Required to merge this edit with the last undo level.
});
const blocks = (0, _blocks.parse)(fileTemplate?.content?.raw);
registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, fileTemplate.id, {
content: serializeBlocks,
blocks,
source: 'theme'
});
if (allowUndo) {
const undoRevert = () => {
registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, edited.id, {
content: serializeBlocks,
blocks: edited.blocks,
source: 'custom'
});
};
registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)('Template reset.'), {
type: 'snackbar',
id: noticeId,
actions: [{
label: (0, _i18n.__)('Undo'),
onClick: undoRevert
}]
});
}
} catch (error) {
const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : (0, _i18n.__)('Template revert failed. Please reload.');
registry.dispatch(_notices.store).createErrorNotice(errorMessage, {
type: 'snackbar'
});
}
};
/**
* Action that removes an array of templates, template parts or patterns.
*
* @param {Array} items An array of template,template part or pattern objects to remove.
*/
exports.revertTemplate = revertTemplate;
const removeTemplates = items => async ({
registry
}) => {
const isResetting = items.every(item => item?.has_theme_file);
const promiseResult = await Promise.allSettled(items.map(item => {
return registry.dispatch(_coreData.store).deleteEntityRecord('postType', item.type, item.id, {
force: true
}, {
throwOnError: true
});
}));
// If all the promises were fulfilled with sucess.
if (promiseResult.every(({
status
}) => status === 'fulfilled')) {
let successMessage;
if (items.length === 1) {
// Depending on how the entity was retrieved its title might be
// an object or simple string.
let title;
if (typeof items[0].title === 'string') {
title = items[0].title;
} else if (typeof items[0].title?.rendered === 'string') {
title = items[0].title?.rendered;
} else if (typeof items[0].title?.raw === 'string') {
title = items[0].title?.raw;
}
successMessage = isResetting ? (0, _i18n.sprintf)( /* translators: The template/part's name. */
(0, _i18n.__)('"%s" reset.'), (0, _htmlEntities.decodeEntities)(title)) : (0, _i18n.sprintf)( /* translators: The template/part's name. */
(0, _i18n.__)('"%s" deleted.'), (0, _htmlEntities.decodeEntities)(title));
} else {
successMessage = isResetting ? (0, _i18n.__)('Items reset.') : (0, _i18n.__)('Items deleted.');
}
registry.dispatch(_notices.store).createSuccessNotice(successMessage, {
type: 'snackbar',
id: 'editor-template-deleted-success'
});
} else {
// If there was at lease one failure.
let errorMessage;
// If we were trying to delete a single template.
if (promiseResult.length === 1) {
if (promiseResult[0].reason?.message) {
errorMessage = promiseResult[0].reason.message;
} else {
errorMessage = isResetting ? (0, _i18n.__)('An error occurred while reverting the item.') : (0, _i18n.__)('An error occurred while deleting the item.');
}
// If we were trying to delete a multiple templates
} else {
const errorMessages = new Set();
const failedPromises = promiseResult.filter(({
status
}) => status === 'rejected');
for (const failedPromise of failedPromises) {
if (failedPromise.reason?.message) {
errorMessages.add(failedPromise.reason.message);
}
}
if (errorMessages.size === 0) {
errorMessage = (0, _i18n.__)('An error occurred while deleting the items.');
} else if (errorMessages.size === 1) {
errorMessage = isResetting ? (0, _i18n.sprintf)( /* translators: %s: an error message */
(0, _i18n.__)('An error occurred while reverting the items: %s'), [...errorMessages][0]) : (0, _i18n.sprintf)( /* translators: %s: an error message */
(0, _i18n.__)('An error occurred while deleting the items: %s'), [...errorMessages][0]);
} else {
errorMessage = isResetting ? (0, _i18n.sprintf)( /* translators: %s: a list of comma separated error messages */
(0, _i18n.__)('Some errors occurred while reverting the items: %s'), [...errorMessages].join(',')) : (0, _i18n.sprintf)( /* translators: %s: a list of comma separated error messages */
(0, _i18n.__)('Some errors occurred while deleting the items: %s'), [...errorMessages].join(','));
}
}
registry.dispatch(_notices.store).createErrorNotice(errorMessage, {
type: 'snackbar'
});
}
};
exports.removeTemplates = removeTemplates;
//# sourceMappingURL=private-actions.js.map