UNPKG

@wordpress/edit-post

Version:
487 lines (442 loc) 11.9 kB
/** * External dependencies */ import { castArray, reduce } from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { apiFetch } from '@wordpress/data-controls'; import { store as interfaceStore } from '@wordpress/interface'; import { controls, dispatch, select, subscribe } from '@wordpress/data'; import { speak } from '@wordpress/a11y'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ import { getMetaBoxContainer } from '../utils/meta-boxes'; import { store as editPostStore } from '.'; /** * Returns an action object used in signalling that the user opened an editor sidebar. * * @param {?string} name Sidebar name to be opened. * * @yield {Object} Action object. */ export function* openGeneralSidebar( name ) { yield controls.dispatch( interfaceStore.name, 'enableComplementaryArea', editPostStore.name, name ); } /** * Returns an action object signalling that the user closed the sidebar. * * @yield {Object} Action object. */ export function* closeGeneralSidebar() { yield controls.dispatch( interfaceStore.name, 'disableComplementaryArea', editPostStore.name ); } /** * Returns an action object used in signalling that the user opened a modal. * * @param {string} name A string that uniquely identifies the modal. * * @return {Object} Action object. */ export function openModal( name ) { return { type: 'OPEN_MODAL', name, }; } /** * Returns an action object signalling that the user closed a modal. * * @return {Object} Action object. */ export function closeModal() { return { type: 'CLOSE_MODAL', }; } /** * Returns an action object used in signalling that the user opened the publish * sidebar. * * @return {Object} Action object */ export function openPublishSidebar() { return { type: 'OPEN_PUBLISH_SIDEBAR', }; } /** * Returns an action object used in signalling that the user closed the * publish sidebar. * * @return {Object} Action object. */ export function closePublishSidebar() { return { type: 'CLOSE_PUBLISH_SIDEBAR', }; } /** * Returns an action object used in signalling that the user toggles the publish sidebar. * * @return {Object} Action object */ export function togglePublishSidebar() { return { type: 'TOGGLE_PUBLISH_SIDEBAR', }; } /** * Returns an action object used to enable or disable a panel in the editor. * * @param {string} panelName A string that identifies the panel to enable or disable. * * @return {Object} Action object. */ export function toggleEditorPanelEnabled( panelName ) { return { type: 'TOGGLE_PANEL_ENABLED', panelName, }; } /** * Returns an action object used to open or close a panel in the editor. * * @param {string} panelName A string that identifies the panel to open or close. * * @return {Object} Action object. */ export function toggleEditorPanelOpened( panelName ) { return { type: 'TOGGLE_PANEL_OPENED', panelName, }; } /** * Returns an action object used to remove a panel from the editor. * * @param {string} panelName A string that identifies the panel to remove. * * @return {Object} Action object. */ export function removeEditorPanel( panelName ) { return { type: 'REMOVE_PANEL', panelName, }; } /** * Returns an action object used to toggle a feature flag. * * @param {string} feature Feature name. * * @return {Object} Action object. */ export function toggleFeature( feature ) { return { type: 'TOGGLE_FEATURE', feature, }; } export function* switchEditorMode( mode ) { yield { type: 'SWITCH_MODE', mode, }; // Unselect blocks when we switch to the code editor. if ( mode !== 'visual' ) { yield controls.dispatch( 'core/block-editor', 'clearSelectedBlock' ); } const message = mode === 'visual' ? __( 'Visual editor selected' ) : __( 'Code editor selected' ); speak( message, 'assertive' ); } /** * Returns an action object used to toggle a plugin name flag. * * @param {string} pluginName Plugin name. * * @return {Object} Action object. */ export function togglePinnedPluginItem( pluginName ) { return { type: 'TOGGLE_PINNED_PLUGIN_ITEM', pluginName, }; } /** * Returns an action object used in signalling that block types by the given * name(s) should be hidden. * * @param {string[]} blockNames Names of block types to hide. * * @return {Object} Action object. */ export function hideBlockTypes( blockNames ) { return { type: 'HIDE_BLOCK_TYPES', blockNames: castArray( blockNames ), }; } /** * Returns an action object used in signaling that a style should be auto-applied when a block is created. * * @param {string} blockName Name of the block. * @param {?string} blockStyle Name of the style that should be auto applied. If undefined, the "auto apply" setting of the block is removed. * * @return {Object} Action object. */ export function updatePreferredStyleVariations( blockName, blockStyle ) { return { type: 'UPDATE_PREFERRED_STYLE_VARIATIONS', blockName, blockStyle, }; } /** * Returns an action object used in signalling that the editor should attempt * to locally autosave the current post every `interval` seconds. * * @param {number} interval The new interval, in seconds. * @return {Object} Action object. */ export function __experimentalUpdateLocalAutosaveInterval( interval ) { return { type: 'UPDATE_LOCAL_AUTOSAVE_INTERVAL', interval, }; } /** * Returns an action object used in signalling that block types by the given * name(s) should be shown. * * @param {string[]} blockNames Names of block types to show. * * @return {Object} Action object. */ export function showBlockTypes( blockNames ) { return { type: 'SHOW_BLOCK_TYPES', blockNames: castArray( blockNames ), }; } let saveMetaboxUnsubscribe; /** * Returns an action object used in signaling * what Meta boxes are available in which location. * * @param {Object} metaBoxesPerLocation Meta boxes per location. * * @yield {Object} Action object. */ export function* setAvailableMetaBoxesPerLocation( metaBoxesPerLocation ) { yield { type: 'SET_META_BOXES_PER_LOCATIONS', metaBoxesPerLocation, }; const postType = yield controls.select( 'core/editor', 'getCurrentPostType' ); if ( window.postboxes.page !== postType ) { window.postboxes.add_postbox_toggles( postType ); } let wasSavingPost = yield controls.select( 'core/editor', 'isSavingPost' ); let wasAutosavingPost = yield controls.select( 'core/editor', 'isAutosavingPost' ); // Meta boxes are initialized once at page load. It is not necessary to // account for updates on each state change. // // See: https://github.com/WordPress/WordPress/blob/5.1.1/wp-admin/includes/post.php#L2307-L2309 const hasActiveMetaBoxes = yield controls.select( editPostStore.name, 'hasMetaBoxes' ); // First remove any existing subscription in order to prevent multiple saves if ( !! saveMetaboxUnsubscribe ) { saveMetaboxUnsubscribe(); } // Save metaboxes when performing a full save on the post. saveMetaboxUnsubscribe = subscribe( () => { const isSavingPost = select( 'core/editor' ).isSavingPost(); const isAutosavingPost = select( 'core/editor' ).isAutosavingPost(); // Save metaboxes on save completion, except for autosaves that are not a post preview. const shouldTriggerMetaboxesSave = hasActiveMetaBoxes && wasSavingPost && ! isSavingPost && ! wasAutosavingPost; // Save current state for next inspection. wasSavingPost = isSavingPost; wasAutosavingPost = isAutosavingPost; if ( shouldTriggerMetaboxesSave ) { dispatch( editPostStore.name ).requestMetaBoxUpdates(); } } ); } /** * Returns an action object used to request meta box update. * * @yield {Object} Action object. */ export function* requestMetaBoxUpdates() { yield { type: 'REQUEST_META_BOX_UPDATES', }; // Saves the wp_editor fields if ( window.tinyMCE ) { window.tinyMCE.triggerSave(); } // Additional data needed for backward compatibility. // If we do not provide this data, the post will be overridden with the default values. const post = yield controls.select( 'core/editor', 'getCurrentPost' ); const additionalData = [ post.comment_status ? [ 'comment_status', post.comment_status ] : false, post.ping_status ? [ 'ping_status', post.ping_status ] : false, post.sticky ? [ 'sticky', post.sticky ] : false, post.author ? [ 'post_author', post.author ] : false, ].filter( Boolean ); // We gather all the metaboxes locations data and the base form data const baseFormData = new window.FormData( document.querySelector( '.metabox-base-form' ) ); const activeMetaBoxLocations = yield controls.select( editPostStore.name, 'getActiveMetaBoxLocations' ); const formDataToMerge = [ baseFormData, ...activeMetaBoxLocations.map( ( location ) => new window.FormData( getMetaBoxContainer( location ) ) ), ]; // Merge all form data objects into a single one. const formData = reduce( formDataToMerge, ( memo, currentFormData ) => { for ( const [ key, value ] of currentFormData ) { memo.append( key, value ); } return memo; }, new window.FormData() ); additionalData.forEach( ( [ key, value ] ) => formData.append( key, value ) ); // Save the metaboxes yield apiFetch( { url: window._wpMetaBoxUrl, method: 'POST', body: formData, parse: false, } ); yield controls.dispatch( editPostStore.name, 'metaBoxUpdatesSuccess' ); } /** * Returns an action object used signal a successful meta box update. * * @return {Object} Action object. */ export function metaBoxUpdatesSuccess() { return { type: 'META_BOX_UPDATES_SUCCESS', }; } /** * Returns an action object used to toggle the width of the editing canvas. * * @param {string} deviceType * * @return {Object} Action object. */ export function __experimentalSetPreviewDeviceType( deviceType ) { return { type: 'SET_PREVIEW_DEVICE_TYPE', deviceType, }; } /** * Returns an action object used to open/close the inserter. * * @param {boolean|Object} value Whether the inserter should be * opened (true) or closed (false). * To specify an insertion point, * use an object. * @param {string} value.rootClientId The root client ID to insert at. * @param {number} value.insertionIndex The index to insert at. * * @return {Object} Action object. */ export function setIsInserterOpened( value ) { return { type: 'SET_IS_INSERTER_OPENED', value, }; } /** * Returns an action object used to switch to template editing. * * @param {boolean} value Is editing template. * @return {Object} Action object. */ export function setIsEditingTemplate( value ) { return { type: 'SET_IS_EDITING_TEMPLATE', value, }; } /** * Potentially create a block based template and switches to the template mode. * * @param {Object?} template template to create and assign before switching. */ export function* __unstableSwitchToTemplateMode( template ) { if ( !! template ) { const savedTemplate = yield controls.dispatch( coreStore, 'saveEntityRecord', 'postType', 'wp_template', template ); const post = yield controls.select( 'core/editor', 'getCurrentPost' ); yield controls.dispatch( coreStore, 'editEntityRecord', 'postType', post.type, post.id, { template: savedTemplate.slug, } ); } yield setIsEditingTemplate( true ); const message = !! template ? __( "Custom template created. You're in template mode now." ) : __( 'Editing template. Changes made here affect all posts and pages that use the template.' ); yield controls.dispatch( noticesStore, 'createSuccessNotice', message, { type: 'snackbar', } ); }