repository = $repository; $this->renderer = $renderer; $this->builtins = $builtins ? $builtins : new BuiltinVariables(); } public function register() { add_action( 'rest_api_init', array( $this, 'routes' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'settings' ) ); } public function routes() { register_rest_route( 'glovario/v1', '/variables', array( 'methods' => 'GET', 'callback' => array( $this, 'index' ), 'permission_callback' => static function () { return current_user_can( 'edit_posts' ); }, 'args' => array( 'search' => array( 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ), 'include' => array( 'sanitize_callback' => 'sanitize_key', 'default' => '', ), 'per_page' => array( 'sanitize_callback' => 'absint', 'default' => 100, 'minimum' => 1, 'maximum' => 100, ), ), ) ); register_rest_route( 'glovario/v1', '/preview', array( 'methods' => 'POST', 'callback' => array( $this, 'preview' ), 'permission_callback' => static function () { return current_user_can( 'edit_posts' ); }, 'args' => array( 'texts' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ), 'required' => true, ), 'post_id' => array( 'sanitize_callback' => 'absint', 'default' => 0, ), ), ) ); } /** * Plain-text previews for builder canvases that show widget text as typed * (Elementor 4's atomic widgets): every Site Variable shortcode and token * is replaced with its current value in the site language. Images become * their alternative text. Unknown keys keep the shortcode's fallback, or the * original text so the editor still sees what was typed. * * @param \WP_REST_Request $request Request with texts (up to 50) and post_id. * @return \WP_REST_Response */ public function preview( $request ) { $texts = array_slice( (array) $request->get_param( 'texts' ), 0, 50 ); $post_id = absint( $request->get_param( 'post_id' ) ); // A post the user cannot edit gives no context (its rules could reveal it). if ( $post_id && ! current_user_can( 'edit_post', $post_id ) ) { $post_id = 0; } $context = array( 'post_id' => $post_id, 'locale' => get_locale(), 'source' => 'editor_preview', ); return rest_ensure_response( array_map( function ( $text ) use ( $context ) { return $this->preview_text( substr( is_string( $text ) ? $text : '', 0, 5000 ), $context ); }, array_values( $texts ) ) ); } private function preview_text( $text, $context ) { $value = static function ( $key, $original, $extra = array() ) use ( $context ) { $resolved = \Glovario\Plugin::instance()->resolver()->resolve( $key, array_merge( $context, $extra ) ); return $resolved ? Renderer::text( $resolved['value'], $resolved['variable']['type'] ) : $original; }; if ( false !== strpos( $text, '[glvr' ) ) { $replaced = preg_replace_callback( '/' . get_shortcode_regex( array( 'glvr' ) ) . '/', static function ( $matches ) use ( $value ) { if ( '[' === $matches[1] && ']' === $matches[6] ) { return substr( $matches[0], 1, -1 ); } $attributes = shortcode_parse_atts( $matches[3] ); $attributes = is_array( $attributes ) ? $attributes : array(); $extra = isset( $attributes['format'] ) ? array( 'date_format' => BuiltinVariables::sanitize_date_format( $attributes['format'] ) ) : array(); return $value( sanitize_key( $attributes['key'] ?? '' ), $attributes['fallback'] ?? $matches[0], $extra ); }, $text ); $text = null === $replaced ? $text : $replaced; } return ( new TokenParser() )->replace( $text, static function ( $key, $original, $date_format ) use ( $value ) { return $value( $key, $original, array( 'date_format' => $date_format ) ); } ); } public function index( $request = null ) { $search = $request instanceof \WP_REST_Request ? sanitize_text_field( $request->get_param( 'search' ) ) : ''; $include = $request instanceof \WP_REST_Request ? sanitize_key( $request->get_param( 'include' ) ) : ''; $per_page = $request instanceof \WP_REST_Request ? min( 100, max( 1, absint( $request->get_param( 'per_page' ) ) ) ) : 100; $variables = array_values( array_filter( $this->builtins->all(), static function ( $variable ) use ( $search, $include ) { if ( $include === $variable['variable_key'] || '' === $search ) { return true; } $haystack = implode( ' ', array( $variable['label'], $variable['variable_key'], $variable['value'], $variable['description'] ) ); return false !== stripos( $haystack, $search ); } ) ); // The site's own variables first: they are picked far more often than the built-ins. $variables = array_merge( $this->repository->all( array( 'number' => $per_page, 'search' => $search, ) ), array_slice( $variables, 0, $per_page ) ); if ( $include && ! in_array( $include, wp_list_pluck( $variables, 'variable_key' ), true ) ) { $selected = $this->builtins->find_by_key( $include ); if ( ! $selected ) { $selected = $this->repository->find_by_key( $include ); } if ( $selected ) { $variables[] = $selected; } } /** * Filters the variables offered by the block editor selector. * * @param array $variables Variable rows. * @param string $search Search text. * @param string $include Key that must be included when it exists. * @param int $per_page Maximum number of site variables. */ $variables = apply_filters( 'glovario_rest_variables', $variables, $search, $include, $per_page ); return rest_ensure_response( array_map( static function ( $variable ) { return array( 'key' => $variable['variable_key'], 'label' => $variable['label'], 'type' => $variable['type'], 'category' => $variable['category_name'] ?? __( 'Uncategorized', 'glovario' ), 'builtin' => ! empty( $variable['builtin'] ), 'value' => wp_html_excerpt( Renderer::text( $variable['value'] ?? '', $variable['type'] ), 80, '…' ), ); }, $variables ) ); } public function settings() { wp_add_inline_script( 'glovario-site-variable-editor-script', 'window.glovarioBlockSettings = ' . wp_json_encode( array( 'canManage' => current_user_can( 'manage_options' ), 'manageUrl' => admin_url( 'admin.php?page=glovario' ), 'createUrl' => admin_url( 'admin.php?page=glovario&action=add' ), 'dateFormats' => $this->builtins->date_format_options(), ) ) . ';', 'before' ); } }