@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
460 lines (459 loc) • 14.5 kB
JavaScript
// packages/editor/src/store/private-actions.js
import { store as coreStore } from "@wordpress/core-data";
import { __, _x, sprintf } from "@wordpress/i18n";
import { store as noticesStore } from "@wordpress/notices";
import { store as blockEditorStore } from "@wordpress/block-editor";
import { store as preferencesStore } from "@wordpress/preferences";
import { addQueryArgs } from "@wordpress/url";
import apiFetch from "@wordpress/api-fetch";
import { parse, __unstableSerializeAndClean } from "@wordpress/blocks";
import { decodeEntities } from "@wordpress/html-entities";
import { dateI18n, getSettings as getDateSettings } from "@wordpress/date";
import isTemplateRevertable from "./utils/is-template-revertable.mjs";
export * from "../dataviews/store/private-actions.mjs";
function setCurrentTemplateId(id) {
return {
type: "SET_CURRENT_TEMPLATE_ID",
id
};
}
var createTemplate = (template) => async ({ select, dispatch, registry }) => {
const savedTemplate = await registry.dispatch(coreStore).saveEntityRecord("postType", "wp_template", template);
registry.dispatch(coreStore).editEntityRecord(
"postType",
select.getCurrentPostType(),
select.getCurrentPostId(),
{
template: savedTemplate.slug
}
);
registry.dispatch(noticesStore).createSuccessNotice(
__("Custom template created. You're in template mode now."),
{
type: "snackbar",
actions: [
{
label: __("Go back"),
onClick: () => dispatch.setRenderingMode(
select.getEditorSettings().defaultRenderingMode
)
}
]
}
);
return savedTemplate;
};
var showBlockTypes = (blockNames) => ({ registry }) => {
const existingBlockNames = registry.select(preferencesStore).get("core", "hiddenBlockTypes") ?? [];
const newBlockNames = existingBlockNames.filter(
(type) => !(Array.isArray(blockNames) ? blockNames : [blockNames]).includes(type)
);
registry.dispatch(preferencesStore).set("core", "hiddenBlockTypes", newBlockNames);
};
var hideBlockTypes = (blockNames) => ({ registry }) => {
const existingBlockNames = registry.select(preferencesStore).get("core", "hiddenBlockTypes") ?? [];
const mergedBlockNames = /* @__PURE__ */ new Set([
...existingBlockNames,
...Array.isArray(blockNames) ? blockNames : [blockNames]
]);
registry.dispatch(preferencesStore).set("core", "hiddenBlockTypes", [...mergedBlockNames]);
};
var saveDirtyEntities = ({
onSave,
dirtyEntityRecords = [],
entitiesToSkip = [],
close,
successNoticeContent
} = {}) => ({ registry }) => {
const PUBLISH_ON_SAVE_ENTITIES = [
{ kind: "postType", name: "wp_navigation" }
];
const saveNoticeId = "site-editor-save-success";
const homeUrl = registry.select(coreStore).getEntityRecord("root", "__unstableBase")?.home;
registry.dispatch(noticesStore).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(coreStore).editEntityRecord(kind, name, key, {
status: "publish"
});
}
pendingSavedRecords.push(
registry.dispatch(coreStore).saveEditedEntityRecord(kind, name, key)
);
}
});
if (siteItemsToSave.length) {
pendingSavedRecords.push(
registry.dispatch(coreStore).__experimentalSaveSpecifiedEntityEdits(
"root",
"site",
void 0,
siteItemsToSave
)
);
}
registry.dispatch(blockEditorStore).__unstableMarkLastChangeAsPersistent();
Promise.all(pendingSavedRecords).then((values) => {
return onSave ? onSave(values) : values;
}).then((values) => {
if (values.some((value) => typeof value === "undefined")) {
registry.dispatch(noticesStore).createErrorNotice(__("Saving failed."));
} else {
registry.dispatch(noticesStore).createSuccessNotice(
successNoticeContent || __("Site updated."),
{
type: "snackbar",
id: saveNoticeId,
actions: [
{
label: __("View site"),
url: homeUrl,
openInNewTab: true
}
]
}
);
}
}).catch(
(error) => registry.dispatch(noticesStore).createErrorNotice(
`${__("Saving failed.")} ${error}`
)
);
};
var revertTemplate = (template, { allowUndo = true } = {}) => async ({ registry }) => {
const noticeId = "edit-site-template-reverted";
registry.dispatch(noticesStore).removeNotice(noticeId);
if (!isTemplateRevertable(template)) {
registry.dispatch(noticesStore).createErrorNotice(__("This template is not revertable."), {
type: "snackbar"
});
return;
}
try {
const templateEntityConfig = registry.select(coreStore).getEntityConfig("postType", template.type);
if (!templateEntityConfig) {
registry.dispatch(noticesStore).createErrorNotice(
__(
"The editor has encountered an unexpected error. Please reload."
),
{ type: "snackbar" }
);
return;
}
const fileTemplatePath = addQueryArgs(
`${templateEntityConfig.baseURL}/${template.id}`,
{ context: "edit", source: template.origin }
);
const fileTemplate = await apiFetch({ path: fileTemplatePath });
if (!fileTemplate) {
registry.dispatch(noticesStore).createErrorNotice(
__(
"The editor has encountered an unexpected error. Please reload."
),
{ type: "snackbar" }
);
return;
}
const serializeBlocks = ({
blocks: blocksForSerialization = []
}) => __unstableSerializeAndClean(blocksForSerialization);
const edited = registry.select(coreStore).getEditedEntityRecord(
"postType",
template.type,
template.id
);
registry.dispatch(coreStore).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 = parse(fileTemplate?.content?.raw);
registry.dispatch(coreStore).editEntityRecord("postType", template.type, fileTemplate.id, {
content: serializeBlocks,
blocks,
source: "theme"
});
if (allowUndo) {
const undoRevert = () => {
registry.dispatch(coreStore).editEntityRecord(
"postType",
template.type,
edited.id,
{
content: serializeBlocks,
blocks: edited.blocks,
source: "custom"
}
);
};
registry.dispatch(noticesStore).createSuccessNotice(__("Template reset."), {
type: "snackbar",
id: noticeId,
actions: [
{
label: __("Undo"),
onClick: undoRevert
}
]
});
}
} catch (error) {
const errorMessage = error.message && error.code !== "unknown_error" ? error.message : __("Template revert failed. Please reload.");
registry.dispatch(noticesStore).createErrorNotice(errorMessage, { type: "snackbar" });
}
};
var removeTemplates = (items) => async ({ registry }) => {
const isResetting = items.every((item) => item?.has_theme_file);
const promiseResult = await Promise.allSettled(
items.map((item) => {
return registry.dispatch(coreStore).deleteEntityRecord(
"postType",
item.type,
item.id,
{ force: true },
{ throwOnError: true }
);
})
);
if (promiseResult.every(({ status }) => status === "fulfilled")) {
let successMessage;
if (items.length === 1) {
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 ? sprintf(
/* translators: %s: The template/part's name. */
__('"%s" reset.'),
decodeEntities(title)
) : sprintf(
/* translators: %s: The template/part's name. */
_x('"%s" deleted.', "template part"),
decodeEntities(title)
);
} else {
successMessage = isResetting ? __("Items reset.") : __("Items deleted.");
}
registry.dispatch(noticesStore).createSuccessNotice(successMessage, {
type: "snackbar",
id: "editor-template-deleted-success"
});
} else {
let errorMessage;
if (promiseResult.length === 1) {
if (promiseResult[0].reason?.message) {
errorMessage = promiseResult[0].reason.message;
} else {
errorMessage = isResetting ? __("An error occurred while reverting the item.") : __("An error occurred while deleting the item.");
}
} else {
const errorMessages = /* @__PURE__ */ 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 = __(
"An error occurred while deleting the items."
);
} else if (errorMessages.size === 1) {
errorMessage = isResetting ? sprintf(
/* translators: %s: an error message */
__(
"An error occurred while reverting the items: %s"
),
[...errorMessages][0]
) : sprintf(
/* translators: %s: an error message */
__(
"An error occurred while deleting the items: %s"
),
[...errorMessages][0]
);
} else {
errorMessage = isResetting ? sprintf(
/* translators: %s: a list of comma separated error messages */
__(
"Some errors occurred while reverting the items: %s"
),
[...errorMessages].join(",")
) : sprintf(
/* translators: %s: a list of comma separated error messages */
__(
"Some errors occurred while deleting the items: %s"
),
[...errorMessages].join(",")
);
}
}
registry.dispatch(noticesStore).createErrorNotice(errorMessage, { type: "snackbar" });
}
};
var setDefaultRenderingMode = (mode) => ({ select, registry }) => {
const postType = select.getCurrentPostType();
const theme = registry.select(coreStore).getCurrentTheme()?.stylesheet;
const renderingModes = registry.select(preferencesStore).get("core", "renderingModes")?.[theme] ?? {};
if (renderingModes[postType] === mode) {
return;
}
const newModes = {
[theme]: {
...renderingModes,
[postType]: mode
}
};
registry.dispatch(preferencesStore).set("core", "renderingModes", newModes);
};
function setStylesPath(path) {
return {
type: "SET_STYLES_PATH",
path
};
}
function setShowStylebook(show) {
return {
type: "SET_SHOW_STYLEBOOK",
show
};
}
function resetStylesNavigation() {
return {
type: "RESET_STYLES_NAVIGATION"
};
}
function setCanvasMinHeight(minHeight) {
return {
type: "SET_CANVAS_MIN_HEIGHT",
minHeight
};
}
function setCurrentRevisionId(revisionId) {
return {
type: "SET_CURRENT_REVISION_ID",
revisionId
};
}
function setShowRevisionDiff(showDiff) {
return {
type: "SET_SHOW_REVISION_DIFF",
showDiff
};
}
var restoreRevision = (revisionId) => async ({ select, dispatch, registry }) => {
const postType = select.getCurrentPostType();
const postId = select.getCurrentPostId();
const entityConfig = registry.select(coreStore).getEntityConfig("postType", postType);
const revisionKey = entityConfig?.revisionKey || "id";
const revision = await registry.resolveSelect(coreStore).getRevision("postType", postType, postId, revisionId, {
context: "edit",
_fields: [
.../* @__PURE__ */ new Set([
"id",
"date",
"modified",
"author",
"meta",
"title.raw",
"excerpt.raw",
"content.raw",
revisionKey
])
].join()
});
if (!revision) {
return;
}
const edits = {
blocks: void 0,
content: revision.content.raw
};
if (revision.title?.raw !== void 0) {
edits.title = revision.title.raw;
}
if (revision.excerpt?.raw !== void 0) {
edits.excerpt = revision.excerpt.raw;
}
if (revision.meta !== void 0) {
edits.meta = revision.meta;
}
dispatch.editPost(edits);
dispatch.setCurrentRevisionId(null);
await dispatch.savePost();
registry.dispatch(noticesStore).createSuccessNotice(
sprintf(
/* translators: %s: Date and time of the revision. */
__("Restored to revision from %s."),
dateI18n(
getDateSettings().formats.datetime,
// Template revisions use the template REST API format, which
// exposes 'modified' instead of 'date'.
revisionKey === "wp_id" ? revision.modified : revision.date
)
),
{
type: "snackbar",
id: "editor-revision-restored"
}
);
};
function selectNote(noteId, options = { focus: false }) {
return {
type: "SELECT_NOTE",
noteId,
options
};
}
export {
createTemplate,
hideBlockTypes,
removeTemplates,
resetStylesNavigation,
restoreRevision,
revertTemplate,
saveDirtyEntities,
selectNote,
setCanvasMinHeight,
setCurrentRevisionId,
setCurrentTemplateId,
setDefaultRenderingMode,
setShowRevisionDiff,
setShowStylebook,
setStylesPath,
showBlockTypes
};
//# sourceMappingURL=private-actions.mjs.map