React to set changes
Hook into every icon set save with easy_svg_pro_set_icons_written: clear caches, export JSON, notify your team and read earlier versions from revisions.
Icon sets change in many places: the set editor, bulk editing, imports, presets, WP-CLI, client mode, the AI abilities. If your theme or plugin builds something from a set, you need to know when it changed. This tutorial shows the one hook that hears every change, how to keep your own code safe when two saves happen at once, and how to read earlier versions from WordPress revisions.
The one place every change passes
Every path that changes a set's icons ends in the same internal function. It stores the icons and then fires easy_svg_pro_set_icons_written. You never have to hook the editor, the importers and WP-CLI separately.
What you get: the set's post ID and the complete list of icons after the save, each with label and sanitized markup. What you do not get: the previous state or a list of changes.
What does not pass through here: creating an empty set, renaming a set, trashing, restoring and deleting. Those change the post, not its icons.
Step by step: a JSON export per set
A common agency case: a headless frontend or a design system wants the icons of a set as a JSON file. We write one file per set into the uploads folder and keep it current.
Hook the action
Register the callback when your plugin loads, and only if Easy SVG Pro is there.
add_action(
'plugins_loaded',
function () {
if ( ! defined( 'EASY_SVG_PRO_FILE' ) ) {
return;
}
add_action( 'easy_svg_pro_set_icons_written', 'acme_export_set_json', 10, 2 );
}
);
Find the collection slug
Templates and frontends address icons as collection/slug. The post ID is not that slug. Easy SVG Pro records the slug per set in an option, keyed by post ID.
function acme_set_collection_slug( $post_id ) {
$slugs = get_option( 'easy_svg_pro_set_collection_slugs', array() );
if ( is_array( $slugs ) && ! empty( $slugs[ (string) $post_id ] ) ) {
return (string) $slugs[ (string) $post_id ];
}
// A set created in this request is recorded on the next page load.
return (string) get_post_field( 'post_name', $post_id );
}
Write the file
function acme_export_set_json( $post_id, $icons ) {
if ( 'publish' !== get_post_status( $post_id ) ) {
return;
}
$uploads = wp_upload_dir();
$dir = trailingslashit( $uploads['basedir'] ) . 'icon-sets';
if ( ! wp_mkdir_p( $dir ) ) {
return;
}
$slug = sanitize_file_name( acme_set_collection_slug( $post_id ) );
$data = array(
'collection' => $slug,
'label' => get_the_title( $post_id ),
'updated' => gmdate( 'c' ),
'icons' => array(),
);
foreach ( $icons as $icon_slug => $icon ) {
$data['icons'][ $icon_slug ] = array(
'label' => (string) ( $icon['label'] ?? $icon_slug ),
'markup' => (string) ( $icon['markup'] ?? '' ),
);
}
file_put_contents( $dir . '/' . $slug . '.json', wp_json_encode( $data ) );
}
The markup is already sanitized and hardened, so it is safe to hand on as it is.
Clean up when a set leaves
The action does not fire on trash or delete. Hook those yourself.
function acme_remove_set_json( $post_id ) {
if ( 'easy_svg_set' !== get_post_type( $post_id ) ) {
return;
}
$uploads = wp_upload_dir();
$file = trailingslashit( $uploads['basedir'] ) . 'icon-sets/' . sanitize_file_name( acme_set_collection_slug( $post_id ) ) . '.json';
if ( file_exists( $file ) ) {
wp_delete_file( $file );
}
}
add_action( 'wp_trash_post', 'acme_remove_set_json' );
add_action( 'before_delete_post', 'acme_remove_set_json' );
Keep slow work out of the save
Your callback runs inside the request that saves the set. A person in the editor, a client in client mode or a CI job waits for it. Anything slow (an HTTP call, image generation, a CDN upload) belongs in a background job:
function acme_queue_set_sync( $post_id, $icons ) {
if ( ! wp_next_scheduled( 'acme_sync_icon_set', array( (int) $post_id ) ) ) {
wp_schedule_single_event( time() + 60, 'acme_sync_icon_set', array( (int) $post_id ) );
}
}
add_action( 'easy_svg_pro_set_icons_written', 'acme_queue_set_sync', 10, 2 );
The check with wp_next_scheduled() folds ten quick saves into one sync. When the job runs, read the set fresh rather than using the $icons from the moment of scheduling.
Writing sets from your own code
If your cron job, admin-post.php handler or webhook changes sets, tell Easy SVG Pro that the request writes, so it takes the set lock first. Without it, two overlapping saves can lose icons.
add_filter(
'easy_svg_pro_set_write_context',
function ( $writes ) {
return $writes || wp_doing_cron();
}
);
REST requests that are not GET and WP-CLI are already covered. See easy_svg_pro_set_write_context for what the lock does and where it does not reach.
To change sets from code, use the REST routes or the abilities, which run through the sanitizer and the lock. Never write the post meta _easy_svg_set_icons yourself: you skip sanitizing, the lock and this action.
Earlier versions with Icon history
Turn on Icon history under Icons → Features and Easy SVG Pro hooks the same action to ask WordPress for a revision after each save. The revisions carry the _easy_svg_set_icons meta, so you can read what a set looked like before. It keeps the last ten revisions per set, and WordPress skips a new revision when nothing changed.
/**
* The icons a set held one save ago, or an empty array.
*/
function acme_previous_set_icons( $post_id ) {
$revisions = wp_get_post_revisions( $post_id, array( 'order' => 'DESC' ) );
$revisions = array_values( $revisions );
// [0] is the save that just happened, [1] the one before.
if ( ! isset( $revisions[1] ) ) {
return array();
}
$raw = get_post_meta( $revisions[1]->ID, '_easy_svg_set_icons', true );
$data = is_string( $raw ) ? json_decode( $raw, true ) : null;
return is_array( $data ) ? $data : array();
}
Call it from a callback at priority 20, so the history callback (priority 10) has already stored the new revision. If the save changed nothing, WordPress made no new revision and [0] is already the state before; compare [0] with the live icons first if that case matters to you.
Revision meta is the raw stored value and is not hardened again when you read it this way. Easy SVG Pro's own history screen runs it through the hardener before it draws anything. Do the same, or only compare it, never print it.
History starts when the feature is switched on. Saves made before that left no revision.
Checklist
- Accept two arguments:
add_action( ..., 10, 2 ). - Treat
$iconsas the full new state, not a diff. - Expect the action even when nothing changed, and on two sets for one bulk move.
- Hook trash, untrash and delete yourself if you build caches.
- Turn the post ID into the collection slug through
easy_svg_pro_set_collection_slugs. - Move slow work into cron.
- Add
easy_svg_pro_set_write_contextif your own code writes sets outside REST and WP-CLI.