Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a hook for plugins to perform tasks once a post is updated but prior it is saved #15568

Closed
s132 opened this issue May 10, 2019 · 1 comment

Comments

@s132
Copy link

s132 commented May 10, 2019

Problem

Currently the JavaScript API doesn't offer a way to perform additional tasks or filter post data once a post is published or updated by the user. This makes post sanitization / validation difficult. Likewise, executing additional tasks on saving (prior updating the database) is notoriously hard.

I'm developing some Gutenberg blocks that would benefit from a pre-save filter or action hook, similar to content_save_pre available through the PHP API. Besides post validation, I have the following use cases for such a hook.

Use cases

  1. Table of contents block
    Once a post is being updated or published, a table of contents (TOC) is generated by iterating over all heading blocks. Thereby, missing anchors are added to all heading blocks and custom properties like "exclude from toc" or "toc short title" are processed.

  2. Extracting data from blocks and storing it as post meta
    Once a post is being updated or published, image props like "caption", "src", "expandable" etc. are fetched from present image blocks and stored as post meta. Thus, a list of images contained in the post is easily accessible for plugins like a lightbox or post gallery page.

In a nutshell, I'd like to be able to modify a post and perform tasks once a post is published or updated, but prior it is written to the database.

Related

Describe the solution you'd like

The solution described in #13413 introduces a new filter that enables asynchronous validation. This is a reasonable idea as it offers great flexibility not only for validation, but any custom task that needs to be performed prior saving a post to the database. Adding a new selector like "willUpdate()" can also mitigate the problem, although this doesn't look right with regard to actions and filters.

Describe alternatives you've considered

My workaround for the TOC uses a subscription, recreating the index immediately after a heading was changed. This works well but is more expensive and complex than it should be. In fact, the index doesn't have to be recreated more than once per save. The same holds for the second use case, which also creates kind of an index.

let blockList = getHeadingBlocks();
wp.data.subscribe(lodash.debounce(() => {
  const currentBlockList = getHeadingBlocks();
  //Fast path: if lengths differ, we have a match
  let changed = currentBlockList.length !== blockList.length;
  if (!changed) {
    for (let i = 0; i < currentBlockList.length; i++) {
      if (currentBlockList[i].attributes !== blockList[i].attributes) {
        changed = true;
        break;
      }
    }
  }
  if (changed) {
    //At least one block changed
    blockList = currentBlockList;
    this.updateIndex();
  }
}, 500, { leading: false, trailing: true }));

For the second use case I considered tracking post updates via isSavingPost(), isAutosavingPost() and didPostSaveRequestSucceed(). If a post has been saved successfully, props are fetched from qualified blocks and stored as post meta via REST API. Obviously this requires a second round trip, is unreliable and far more complex than using a filter or action hook to accomplish this kind of task.

watch() {
  const { isSaving, isAutosaving, success } = this.props;
  let wasSaving = this.state.wasSaving || isSaving && !isAutosaving;
  if(wasSaving) {
    if(success) {
      this.updateMeta();
        wasSaving = false;
      }
  }
  if(wasSaving !== this.state.wasSaving) {
    this.setState({ wasSaving: wasSaving });
  }
}

updateMeta() {
  const id = select('core/editor').getCurrentPostId(); 
  wp.apiFetch({
    path: '/wp/v2/posts/' + id,
    method: 'POST',
    data: getImageMeta()
  }).catch(error => {
    dispatch('core/notices').createNotice(
      'error', 
      'meta update failed', 
      { isDismissible: true }
    );
  });
}
@youknowriad
Copy link
Contributor

Unless I'm mistaken this is a duplicate of #13413

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants