true, 'single' => true, 'type' => 'string', 'default' => '', 'object_subtype' => $postType, 'prepare_callback' => 'sanitize_text_field', 'sanitize_callback' => 'sanitize_text_field', 'auth_callback' => fn(): bool => current_user_can('edit_posts'), ]; foreach ($keys as $key) { register_meta('post', $key, $options); } } } } /** * Make all of our custom fields private, so they don't appear in the * "Custom Fields" panel, which can cause conflicts for the Block Editor. * * https://github.com/WordPress/gutenberg/issues/23078 * * @since 4.0.0 * @since 6.0.0 Make static. * @since 6.0.1 Accept null params from WP core. */ public static function isProtectedMeta($protected, $metaKey) { if ($metaKey === null) { return (bool) $protected; } $keysToProtect = CoreUtils::getPostMetaKeys('all'); if (in_array($metaKey, $keysToProtect, true)) { return true; } return (bool) $protected; } /** * On trash post. * * We attempt to send a DELETE REST API request when a post is trashed so the audio * no longer appears in playlists, or in the publishers BeyondWords dashboard. * * @since 3.9.0 Introduced. * @since 5.4.0 Renamed from onTrashOrDeletePost, and we now remove all * BeyondWords data when a post is trashed. * @since 6.0.0 Make static. * * @param int $postId Post ID. **/ public static function onTrashPost($postId) { $postId = (int) $postId; // Skip posts that don't have BeyondWords content (e.g. revisions, Jetpack sitemaps) if (! PostMetaUtils::hasContent($postId)) { return; } ApiClient::deleteAudio($postId); PostMetaUtils::removeAllBeyondwordsMetadata($postId); } /** * On delete post. * * We attempt to send a DELETE REST API request when a post is deleted so the audio * no longer appears in playlists, or in the publishers BeyondWords dashboard. * * @since 5.4.0 Introduced, replacing onTrashOrDeletePost. * @since 6.0.0 Make static. * * @param int $postId Post ID. **/ public static function onDeletePost($postId) { $postId = (int) $postId; // Skip posts that don't have BeyondWords content (e.g. revisions, Jetpack sitemaps) if (! PostMetaUtils::hasContent($postId)) { return; } ApiClient::deleteAudio($postId); } /** * WP Save Post action. * * Fires after a post, its terms and meta data has been saved. * * @since 3.0.0 * @since 3.2.0 Added beyondwords_post_statuses filter. * @since 3.6.1 Improve $postBefore hash comparison. * @since 3.9.0 Renamed method from wpAfterInsertPost to onAddOrUpdatePost. * @since 4.0.0 Removed hash comparison. * @since 4.4.0 Delete audio if beyondwords_delete_content custom field is set. * @since 4.5.0 Remove unwanted debugging custom fields. * @since 5.1.0 Move post status check out of here. * @since 6.0.0 Make static and refactor for Magic Embed updates. * @since 6.0.5 Skip second wp_after_insert_post triggered by Gutenberg's meta box save. * * @param int $postId Post ID. **/ public static function onAddOrUpdatePost($postId) { $postId = (int) $postId; // Gutenberg fires wp_after_insert_post twice: once via the REST API, // and again via a backward-compatible meta box save POST to post.php. // Skip the second (redundant) request to prevent duplicate API calls. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if (! empty($_REQUEST['meta-box-loader'])) { return false; } // Has the "Remove" feature been used? if (get_post_meta($postId, 'beyondwords_delete_content', true) === '1') { // Make DELETE API request self::deleteAudioForPost($postId); // Remove custom fields PostMetaUtils::removeAllBeyondwordsMetadata($postId); return false; } return (bool) self::generateAudioForPost($postId); } /** * Get the language code from a JSON mapping if it is empty. * * @since 5.4.0 Introduced. * @since 6.0.0 Make static. * @since 6.0.1 Accept a null $meta_key parameter. * * @param mixed $value The value of the metadata. * @param int $object_id The ID of the object metadata is for. * @param ?string $meta_key The key of the metadata. * * @return mixed The metadata value. */ public static function getLangCodeFromJsonIfEmpty($value, $object_id, $meta_key) { if ('beyondwords_language_code' === $meta_key && empty($value)) { $languageId = get_post_meta($object_id, 'beyondwords_language_id', true); if ($languageId) { $langCodes = json_decode(file_get_contents(BEYONDWORDS__PLUGIN_DIR . 'assets/lang-codes.json'), true); if (is_array($langCodes) && array_key_exists($languageId, $langCodes)) { return [$langCodes[$languageId]]; } } } return $value; } }