Source: php/sync/class-delete-sync.php

<?php
/**
 * Delete Sync to Cloudinary.
 *
 * @package Cloudinary
 */

namespace Cloudinary\Sync;

use Cloudinary\Sync;
use Cloudinary\Utils;

/**
 * Class Delete_Sync.
 *
 * Push media to Cloudinary on upload.
 */
class Delete_Sync {

	/**
	 * Holds the plugin instance.
	 *
	 * @since   0.1
	 *
	 * @var     \Cloudinary\Plugin Instance of the global plugin.
	 */
	protected $plugin;

	/**
	 * Delete_Sync constructor.
	 *
	 * @param \Cloudinary\Plugin $plugin The plugin.
	 */
	public function __construct( \Cloudinary\Plugin $plugin ) {
		$this->plugin = $plugin;
	}

	/**
	 * Register any hooks that this component needs.
	 */
	private function register_hooks() {
		add_action( 'delete_attachment', array( $this, 'delete_asset' ), 10 );
		add_filter( 'user_has_cap', array( $this, 'can_delete_asset' ), 10, 3 );
	}

	/**
	 * Checks if an image is synced before allowing a user to have rights to delete the file.
	 *
	 * @param array $all_caps All capabilities for the user.
	 * @param array $caps     Current requested capabilities.
	 * @param array $args     Additional args for the check.
	 *
	 * @return array
	 */
	public function can_delete_asset( $all_caps, $caps, $args ) {

		if ( 3 === count( $args ) ) {
			// The args are indexed, list them in named variables to better understand.
			list( $request_cap, , $post_id ) = $args;

			if ( $this->plugin->components['media']->is_media( $post_id ) && 'delete_post' === $request_cap && ! empty( $all_caps['delete_posts'] ) ) {

				// Check if is pending.
				if ( ! $this->plugin->components['sync']->is_synced( $post_id ) && $this->plugin->components['sync']->is_pending( $post_id ) ) {
					// Check for errors.
					$has_error = get_post_meta( $post_id, Sync::META_KEYS['sync_error'], true );
					if ( empty( $has_error ) ) {
						$all_caps['delete_posts'] = false;
						$action                   = Utils::get_sanitized_text( 'action' );
						if ( ! empty( $action ) && 'delete' === $action ) {
							wp_die( esc_html__( 'Sorry, you can’t delete an asset until it has fully synced with Cloudinary. Try again once syncing is complete.', 'cloudinary' ) );
						}
					}
				}
			}
		}

		return $all_caps;
	}

	/**
	 * Delete an asset on Cloudinary.
	 *
	 * @param int $post_id The post id to delete asset for.
	 */
	public function delete_asset( $post_id ) {
		// In some environments, the $post_id is a string, failing ahead on a strict compare.
		// For that reason we need to ensure the variable type.
		$post_id = absint( $post_id );

		if ( $this->plugin->components['sync']->is_synced( $post_id ) ) {

			// check if this is not a transformation base image.
			$public_id = $this->plugin->components['media']->get_public_id( $post_id, true );
			$linked    = $this->plugin->components['media']->get_linked_attachments( $public_id );
			if ( count( $linked ) > 1 ) {
				// There are other attachments sharing this public_id, so skip it.
				return;
			}
			if ( count( $linked ) === 1 && $post_id !== $linked[0] ) {
				// Something odd is up. skip it in case.
				return;
			}
			// Next we need to check that the file is in the cloudinary folder.
			$path              = trim( Utils::pathinfo( $public_id, PATHINFO_DIRNAME ), '.' );
			$cloudinary_folder = $this->plugin->settings->get_value( 'cloudinary_folder' );
			if ( $cloudinary_folder === $path ) {
				$type    = $this->plugin->components['media']->get_resource_type( $post_id );
				$options = array(
					'public_id'  => $public_id,
					'invalidate' => true, // clear from CDN cache as well.
				);
				// Not a background request, since the post could be deleted before the background request hits causing it to not find the post and therefore not finding the public_id
				// using the public_id directly in a background call, would make validation complicated since there is no longer a post to validate against.
				$this->plugin->components['connect']->api->destroy( $type, $options );
			}
			/**
			 * Action fired when deleting a synced asset.
			 *
			 * @hook   cloudinary_delete_asset
			 * @since  3.0.1
			 */
			do_action( 'cloudinary_delete_asset', $post_id );
		}
	}

	/**
	 * Setup this component.
	 */
	public function setup() {
		$this->register_hooks();
	}
}