PluginProbe
AI / trunk
AI vtrunk
1.3.0 1.2.0 1.1.0 1.0.2 1.0.1 1.0.0 0.9.0 trunk 0.1.1 0.2.0 0.2.1 0.3.0 0.3.1 0.4.0 0.4.1 0.5.0 0.6.0 0.7.0 0.8.0
ai / includes / Experiments / Meta_Description / Meta_Description.php

Meta_Description.php in AI trunk, at includes/Experiments/Meta_Description/Meta_Description.php

218 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Meta description experiment implementation.
4 *
5 * @package WordPress\AI
6 */
7
8 declare( strict_types=1 );
9
10 namespace WordPress\AI\Experiments\Meta_Description;
11
12 use WordPress\AI\Abilities\Meta_Description\Meta_Description as Meta_Description_Ability;
13 use WordPress\AI\Abilities\Meta_Description\SEO_Integration;
14 use WordPress\AI\Abstracts\Abstract_Feature;
15 use WordPress\AI\Asset_Loader;
16 use WordPress\AI\Experiments\Experiment_Category;
17
18 use function WordPress\AI\get_min_content_length;
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 /**
25 * Meta description experiment.
26 *
27 * Provides AI-generated meta description suggestions in the post editor with
28 * automatic SEO plugin integration for storing descriptions in the correct meta field.
29 *
30 * @since 0.7.0
31 */
32 class Meta_Description extends Abstract_Feature {
33
34 /**
35 * {@inheritDoc}
36 */
37 public static function get_id(): string {
38 return 'meta-description';
39 }
40
41 /**
42 * {@inheritDoc}
43 */
44 protected function load_metadata(): array {
45 return array(
46 'label' => __( 'Meta Description Generation', 'ai' ),
47 'description' => __( 'Generates meta description suggestions and integrates those with various SEO plugins. Requires an AI connector that includes support for text generation models.', 'ai' ),
48 'category' => Experiment_Category::EDITOR,
49 );
50 }
51
52 /**
53 * {@inheritDoc}
54 *
55 * @since 0.7.0
56 */
57 public function register(): void {
58 add_action( 'wp_abilities_api_init', array( $this, 'register_abilities' ) );
59 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
60 add_action( 'deactivated_plugin', array( $this, 'clear_active_plugin_cache' ) );
61
62 $this->maybe_output_meta_description();
63 $this->register_post_meta();
64 }
65
66 /**
67 * Registers the meta description ability.
68 *
69 * @since 0.7.0
70 */
71 public function register_abilities(): void {
72 wp_register_ability(
73 'ai/' . $this->get_id(),
74 array(
75 'label' => $this->get_label(),
76 'description' => $this->get_description(),
77 'ability_class' => Meta_Description_Ability::class,
78 ),
79 );
80 }
81
82 /**
83 * Registers the fallback post meta key for REST API access.
84 *
85 * This ensures the meta key is accessible through the WordPress data layer
86 * when no SEO plugin is active to manage it.
87 *
88 * @since 0.7.0
89 */
90 public function register_post_meta(): void {
91 $meta_key = SEO_Integration::get_meta_key();
92 $seo_plugin = SEO_Integration::detect_active_plugin();
93
94 // Only register the fallback meta key. SEO plugins register their own.
95 if ( null !== $seo_plugin ) {
96 return;
97 }
98
99 $post_types = get_post_types( array( 'show_in_rest' => true ), 'names' );
100
101 foreach ( $post_types as $post_type ) {
102 if ( 'attachment' === $post_type ) {
103 continue;
104 }
105
106 register_post_meta(
107 $post_type,
108 $meta_key,
109 array(
110 'show_in_rest' => true,
111 'single' => true,
112 'type' => 'string',
113 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) {
114 return current_user_can( 'edit_post', $post_id );
115 },
116 )
117 );
118 }
119 }
120
121 /**
122 * Enqueues and localizes the admin script.
123 *
124 * @since 0.7.0
125 *
126 * @param string $hook_suffix The current admin page hook suffix.
127 */
128 public function enqueue_assets( string $hook_suffix ): void {
129 if ( 'post.php' !== $hook_suffix && 'post-new.php' !== $hook_suffix ) {
130 return;
131 }
132
133 $screen = get_current_screen();
134
135 if (
136 ! $screen ||
137 ! in_array( $screen->post_type, get_post_types( array( 'show_in_rest' => true ), 'names' ), true ) ||
138 'attachment' === $screen->post_type
139 ) {
140 return;
141 }
142
143 $seo_plugin = SEO_Integration::detect_active_plugin();
144
145 Asset_Loader::enqueue_script( 'meta_description', 'experiments/meta-description', array( 'include_core_abilities' => true ) );
146 Asset_Loader::enqueue_style( 'meta_description', 'experiments/meta-description' );
147 Asset_Loader::localize_script(
148 'meta_description',
149 'MetaDescriptionData',
150 array(
151 'enabled' => $this->is_enabled(),
152 'metaKey' => SEO_Integration::get_meta_key( $seo_plugin ),
153 'seoPlugin' => $seo_plugin,
154 'minContentLength' => get_min_content_length( 'meta-description', 250 ),
155 )
156 );
157 }
158
159 /**
160 * Clears the active SEO plugin cache when a plugin is deactivated.
161 *
162 * @since 0.7.0
163 */
164 public function clear_active_plugin_cache(): void {
165 delete_transient( 'wpai_active_seo_plugin' );
166 }
167
168 /**
169 * Determines if the meta description should be rendered by the experiment.
170 *
171 * @since 0.7.0
172 */
173 protected function maybe_output_meta_description(): void {
174 if ( ! $this->is_enabled() ) {
175 return;
176 }
177
178 $seo_plugin = SEO_Integration::detect_active_plugin();
179
180 // Let the SEO plugin handle the output if found.
181 if ( ! empty( $seo_plugin ) ) {
182 return;
183 }
184
185 add_action( 'wp_head', array( $this, 'output_meta_description' ), 1 );
186 }
187
188 /**
189 * Injects the meta description into the head if it exists.
190 *
191 * @since 0.7.0
192 */
193 public function output_meta_description(): void {
194 if ( ! is_singular() ) {
195 return;
196 }
197
198 $meta_description = get_post_meta( (int) get_the_ID(), SEO_Integration::get_meta_key(), true );
199
200 /**
201 * Filter the meta description output.
202 * An empty string will prevent output.
203 *
204 * @since 0.7.0
205 *
206 * @param string $meta_description The meta description.
207 * @return string The filtered meta description.
208 */
209 $meta_description = apply_filters( 'wpai_meta_description', $meta_description );
210
211 if ( empty( $meta_description ) ) {
212 return;
213 }
214
215 echo '<meta name="description" content="' . esc_attr( $meta_description ) . '" />';
216 }
217 }
218