Skip to content

Commit 49c708e

Browse files
committed
feat: add studio-first wordpress connect flow
1 parent 904c15c commit 49c708e

File tree

9 files changed

+337
-38
lines changed

9 files changed

+337
-38
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
## [1.2.4] - 2026-04-13
8+
9+
### Added
10+
- Studio-first WordPress handoff: the plugin can now consume one-time `aeo_connect` tokens issued by Studio and complete the connection without manual API-key copy/paste
11+
- Public `/wp-json/aeo/v1/status` health endpoint with connection metadata for Studio install/connect detection
12+
13+
### Changed
14+
- REST API now serves both `aeo/v1` and `aeocas/v1` namespaces during the migration window so old and new platform clients can coexist
15+
16+
### Fixed
17+
- Studio and plugin contract mismatch that prevented direct draft-publish flows from recognizing an already connected WordPress site
18+
719
## [1.2.2] - 2026-04-12
820

921
### Added

admin/views/audit-page.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
$aeocas_notice = isset( $_GET['aeocas_notice'] ) ? sanitize_key( wp_unslash( $_GET['aeocas_notice'] ) ) : '';
2929
$aeocas_requested_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';
3030
// phpcs:enable WordPress.Security.NonceVerification.Recommended
31+
$aeocas_connect_notice = AEOCAS_Settings::consume_connect_notice();
3132
$aeocas_module_labels = array(
3233
'content' => array(
3334
'label' => 'Content Publishing',
@@ -362,6 +363,20 @@
362363
<!-- Error state (rendered by JS when needed) -->
363364
<div id="aeo-audit-error"></div>
364365

366+
<?php if ( is_array( $aeocas_connect_notice ) && ! empty( $aeocas_connect_notice['message'] ) ) : ?>
367+
<?php
368+
$aeocas_connect_notice_class = 'notice-info';
369+
if ( 'success' === $aeocas_connect_notice['type'] ) {
370+
$aeocas_connect_notice_class = 'notice-success';
371+
} elseif ( 'warning' === $aeocas_connect_notice['type'] ) {
372+
$aeocas_connect_notice_class = 'notice-warning';
373+
} elseif ( 'error' === $aeocas_connect_notice['type'] ) {
374+
$aeocas_connect_notice_class = 'notice-error';
375+
}
376+
?>
377+
<div class="notice <?php echo esc_attr( $aeocas_connect_notice_class ); ?> is-dismissible"><p><?php echo esc_html( $aeocas_connect_notice['message'] ); ?></p></div>
378+
<?php endif; ?>
379+
365380
<!-- Re-audit progress -->
366381
<div id="aeo-reaudit-progress" style="display: none;">
367382
<div class="aeo-reaudit-bar">

aeo-content-ai-studio.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* Plugin Name: AEO Content AI Studio
44
* Description: AI Engine Optimization for WordPress. Connects your site to AEO Content AI Studio for AI-powered content management and audit reports.
5-
* Version: 1.2.3
5+
* Version: 1.2.4
66
* Author: AEO Content, Inc.
77
* Author URI: https://www.aeocontent.ai
88
* License: GPL v2 or later
@@ -17,7 +17,7 @@
1717
exit;
1818
}
1919

20-
define( 'AEOCAS_VERSION', '1.2.3' );
20+
define( 'AEOCAS_VERSION', '1.2.4' );
2121
define( 'AEOCAS_PLUGIN_FILE', __FILE__ );
2222
define( 'AEOCAS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
2323
define( 'AEOCAS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

includes/class-aeo-rest-api.php

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* REST API endpoints under /wp-json/aeocas/v1/.
3+
* REST API endpoints under /wp-json/aeo/v1/ with /wp-json/aeocas/v1/ aliases.
44
*
55
* All mutating endpoints require authenticated platform requests.
66
*/
@@ -14,29 +14,41 @@ class AEOCAS_Rest_Api {
1414
/** @var AEOCAS_Plugin */
1515
private $plugin;
1616

17-
const REST_NAMESPACE = 'aeocas/v1';
17+
const REST_NAMESPACE = 'aeo/v1';
18+
const LEGACY_REST_NAMESPACE = 'aeocas/v1';
1819

1920
public function __construct( $plugin ) {
2021
$this->plugin = $plugin;
2122
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
2223
}
2324

25+
public static function get_rest_namespaces() {
26+
return array(
27+
self::REST_NAMESPACE,
28+
self::LEGACY_REST_NAMESPACE,
29+
);
30+
}
31+
32+
private function register_route( $route, $args ) {
33+
foreach ( self::get_rest_namespaces() as $namespace ) {
34+
register_rest_route( $namespace, $route, $args );
35+
}
36+
}
37+
2438
public function register_routes() {
2539
// Public health check.
26-
register_rest_route(
27-
self::REST_NAMESPACE,
40+
$this->register_route(
2841
'/status',
2942
array(
3043
'methods' => 'GET',
3144
'callback' => array( $this, 'handle_status' ),
32-
'permission_callback' => array( $this, 'check_auth' ),
45+
'permission_callback' => array( $this, 'allow_public_request' ),
3346
'args' => array(),
3447
)
3548
);
3649

3750
// Unified command dispatch.
38-
register_rest_route(
39-
self::REST_NAMESPACE,
51+
$this->register_route(
4052
'/command',
4153
array(
4254
'methods' => 'POST',
@@ -56,8 +68,7 @@ public function register_routes() {
5668
);
5769

5870
// Activity log (authenticated platform requests).
59-
register_rest_route(
60-
self::REST_NAMESPACE,
71+
$this->register_route(
6172
'/logs',
6273
array(
6374
'methods' => 'GET',
@@ -95,8 +106,7 @@ public function register_routes() {
95106
);
96107

97108
// Posts list (read).
98-
register_rest_route(
99-
self::REST_NAMESPACE,
109+
$this->register_route(
100110
'/posts',
101111
array(
102112
'methods' => 'GET',
@@ -138,8 +148,7 @@ public function register_routes() {
138148
);
139149

140150
// Single post (read).
141-
register_rest_route(
142-
self::REST_NAMESPACE,
151+
$this->register_route(
143152
'/posts/(?P<id>\d+)',
144153
array(
145154
'methods' => 'GET',
@@ -155,8 +164,7 @@ public function register_routes() {
155164
);
156165

157166
// Publish endpoint.
158-
register_rest_route(
159-
self::REST_NAMESPACE,
167+
$this->register_route(
160168
'/publish',
161169
array(
162170
'methods' => 'POST',
@@ -167,8 +175,7 @@ public function register_routes() {
167175
);
168176

169177
// Categories list.
170-
register_rest_route(
171-
self::REST_NAMESPACE,
178+
$this->register_route(
172179
'/categories',
173180
array(
174181
'methods' => 'GET',
@@ -201,8 +208,7 @@ public function register_routes() {
201208
);
202209

203210
// Tags list.
204-
register_rest_route(
205-
self::REST_NAMESPACE,
211+
$this->register_route(
206212
'/tags',
207213
array(
208214
'methods' => 'GET',
@@ -238,17 +244,27 @@ public function check_auth( $request ) {
238244
return AEOCAS_Auth::verify_request( $request );
239245
}
240246

247+
public function allow_public_request() {
248+
return true;
249+
}
250+
241251
// ─── Status ───────────────────────────────────────────────
242252

243253
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- REST callback signature includes the request argument for consistency.
244254
public function handle_status( $request ) {
255+
$connected = ! empty( get_option( 'aeocas_site_token', '' ) ) && get_option( 'aeocas_connection_verified', false );
256+
245257
return rest_ensure_response(
246258
array(
247-
'ok' => true,
248-
'version' => AEOCAS_VERSION,
249-
'features' => $this->plugin->get_enabled_features(),
250-
'site_url' => get_site_url(),
251-
'home_url' => get_home_url(),
259+
'ok' => true,
260+
'version' => AEOCAS_VERSION,
261+
'plugin_version' => AEOCAS_VERSION,
262+
'connected' => (bool) $connected,
263+
'namespace' => self::REST_NAMESPACE,
264+
'namespaces' => self::get_rest_namespaces(),
265+
'features' => $this->plugin->get_enabled_features(),
266+
'site_url' => get_site_url(),
267+
'home_url' => get_home_url(),
252268
)
253269
);
254270
}

0 commit comments

Comments
 (0)