Value => label. */ public static function choices() { return array( 'after' => __( 'Inline, after the content', 'asklet' ), 'before' => __( 'Inline, before the content', 'asklet' ), 'panel' => __( 'Side panel', 'asklet' ), 'slidein' => __( 'Slide-in card', 'asklet' ), 'hidden' => __( 'Hidden on this post', 'asklet' ), ); } /** * The stored values, without labels (no __() outside a request that shows them). * * @return string[] */ private static function values() { return array( 'after', 'before', 'panel', 'slidein', 'hidden' ); } /** * The post's own choice, or '' when it follows the site default. * * @param int $post_id Post ID. * @return string */ public static function get( $post_id ) { return self::sanitize_value( get_post_meta( $post_id, self::META, true ) ); } /** * Keeps only known values; anything else means "site default". * * @param mixed $value Raw value. * @return string */ public static function sanitize_value( $value ) { return is_string( $value ) && in_array( $value, self::values(), true ) ? $value : ''; } /** * Whether the block editor panel can save this post type's meta: REST only exposes meta for * post types that support `custom-fields`. The classic editor shows the meta box either way. * * @param string $post_type Post type name. * @return bool */ public static function has_editor_panel( $post_type ) { return array_key_exists( $post_type, Asklet_Settings::public_post_types() ) && post_type_supports( $post_type, 'custom-fields' ); } /** * Registers the meta for every public post type, editable over REST by anyone who can edit * the post (the panel saves through it). */ public static function register_meta() { foreach ( array_keys( Asklet_Settings::public_post_types() ) as $post_type ) { register_post_meta( $post_type, self::META, array( 'type' => 'string', 'single' => true, 'default' => '', 'sanitize_callback' => array( __CLASS__, 'sanitize_value' ), 'auth_callback' => array( __CLASS__, 'can_edit_meta' ), 'show_in_rest' => array( 'schema' => array( 'type' => 'string', 'enum' => array_merge( array( '' ), self::values() ), ), ), ) ); } } /** * Meta auth callback: the user must be able to edit the post. * * @param bool $allowed Whether the user can edit the meta (ignored: it is protected meta). * @param string $meta_key Meta key. * @param int $post_id Post ID. * @return bool */ public static function can_edit_meta( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_post', $post_id ); } /** * The "Site default (…)" label for a post type, as the editor shows it. * * @param string $post_type Post type name. * @return string */ private static function default_label( $post_type ) { $settings = Asklet_Settings::get(); $enabled = in_array( $post_type, $settings['post_types'], true ); $default = $enabled && 'manual' !== $settings['display'] ? Asklet_Settings::displays()[ $settings['display'] ] : __( 'not shown', 'asklet' ); /* translators: %s: the site-wide display, e.g. "Side panel" */ return sprintf( __( 'Site default (%s)', 'asklet' ), $default ); } /** * Loads the sidebar panel in the post editor (not the site or widget editors). */ public static function enqueue_editor_assets() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || 'post' !== $screen->base || ! self::has_editor_panel( $screen->post_type ) ) { return; } $choices = array(); foreach ( self::choices() as $value => $label ) { $choices[] = array( 'value' => $value, 'label' => $label, ); } wp_enqueue_script( 'asklet-editor', plugin_dir_url( ASKLET_PLUGIN_FILE ) . 'assets/editor.js', array( 'wp-components', 'wp-data', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-plugins' ), ASKLET_VERSION, true ); wp_add_inline_script( 'asklet-editor', 'window.askletEditor = ' . wp_json_encode( array( 'metaKey' => self::META, 'defaultLabel' => self::default_label( $screen->post_type ), 'choices' => $choices, ) ) . ';', 'before' ); wp_set_script_translations( 'asklet-editor', 'asklet' ); } /** * Adds the box to every public post type, so one page can get the survey even when its post * type is off site-wide. Where the block editor panel works, the box is for the classic * editor only. * * @param string $post_type Current post type. */ public static function add_meta_box( $post_type ) { if ( ! array_key_exists( $post_type, Asklet_Settings::public_post_types() ) ) { return; } add_meta_box( 'asklet', __( 'Asklet', 'asklet' ), array( __CLASS__, 'render' ), $post_type, 'side', 'low', array( '__back_compat_meta_box' => self::has_editor_panel( $post_type ) ) ); } /** * Renders the select. * * @param WP_Post $post Post being edited. */ public static function render( $post ) { $current = self::get( $post->ID ); wp_nonce_field( self::NONCE, self::NONCE ); echo '
'; echo ''; echo '' . esc_html__( 'An Asklet block or [asklet] shortcode in the post replaces this: the survey then shows only there.', 'asklet' ) . '
'; } /** * Saves the meta box select (the block editor panel saves over REST instead). * * @param int $post_id Post ID. */ public static function save( $post_id ) { if ( ! isset( $_POST[ self::NONCE ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE ] ) ), self::NONCE ) ) { return; } if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) ) { return; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } $value = isset( $_POST['asklet_display'] ) ? self::sanitize_value( sanitize_key( wp_unslash( $_POST['asklet_display'] ) ) ) : ''; if ( '' !== $value ) { update_post_meta( $post_id, self::META, $value ); } else { delete_post_meta( $post_id, self::META ); } } }