* * The name of the source and of its argument are part of the data contract with Pro (they are in the content of * posts and templates): Pro registers the same source. * * @since 1.0.0 */ final class BlockBindings { public const SOURCE = 'aop/option'; /** * The attributes the editor gets a preview value for (see editorData()). */ private const PREVIEW_ATTRIBUTES = ['content', 'text', 'caption', 'url', 'alt', 'title', 'id', 'datetime', 'linkTarget', 'rel']; /** * Register on init. Not when Pro has taken over (it registers the source itself), not on a WordPress without * Block Bindings, and not again. */ public static function register(): void { if (Plugin::isDisabled() || !function_exists('register_block_bindings_source')) { return; } // Registered twice is a "doing it wrong" of WordPress (init that runs again, or another plugin first). if (get_block_bindings_source(self::SOURCE) !== null) { return; } register_block_bindings_source(self::SOURCE, [ 'label' => self::label(), 'get_value_callback' => [self::class, 'value'], ]); } public static function label(): string { return __('Admin Options Pages', 'admin-options-pages'); } /** * The value WordPress puts in the block, or null to keep what the block has. * * @param array $args The `args` of the binding: `name` is the option name of the field. * @param \WP_Block $block The block (not used). * @param string $attribute The bound attribute. */ public static function value(array $args, $block, string $attribute): ?string { $name = isset($args['name']) && is_string($args['name']) ? $args['name'] : ''; return $name === '' ? null : Output::binding($name, $attribute); } /** * The script that registers the source in the block editor and the site editor (field list and preview). */ public static function enqueueEditor(): void { if (Plugin::isDisabled() || !current_user_can('edit_posts')) { return; } $handle = Plugin::PREFIX . 'bindings-js'; Enqueue::buildScript($handle, 'bindings'); Enqueue::scriptTranslations($handle); wp_add_inline_script($handle, 'window.aopBindings = ' . wp_json_encode(self::editorData()) . ';', 'before'); } /** * What the editor needs: the public fields, with a label and the values the website would show, so a bound block * looks in the editor as it will on the website. Only public fields: their values are on the website anyway. * * @return array{source: string, label: string, fields: array}>} */ public static function editorData(): array { $fields = []; // Built from the stored pages, not from the kept list: in the editor a field that is gone (a page deleted // with SQL, by a script or another plugin) must not be offered. One query, in the admin only. foreach (PublicFields::rebuild() as $name => $field) { if (PublicFields::get($name) === null) { continue; } $values = []; foreach (self::PREVIEW_ATTRIBUTES as $attribute) { $value = Output::binding($name, $attribute); if ($value !== null) { $values[$attribute] = $value; } } $label = isset($field['field_label']) ? trim(stripslashes((string) $field['field_label'])) : ''; $fields[] = [ 'name' => (string) $name, 'label' => $label !== '' ? sprintf('%s (%s)', $label, $name) : (string) $name, 'values' => $values, ]; } return ['source' => self::SOURCE, 'label' => self::label(), 'fields' => $fields]; } }