Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

AI Launchpad: make tasks completable from wp-admin on Simple and Atomic — honor the catalog visibility gate, add local completion listeners (Jetpack Social, About page), retrieve real signals on Atomic (subscriber counts, memberships), and complete acknowledgment / no-signal tasks on CTA click (including a "Mark as complete" button and an SSH task). Point the social and design CTAs at wp-admin, hide the Jetpack Social tasks on private sites where their admin page isn't available, and add an ?all_tasks=1 testing param that renders the full task catalog.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
// request regardless of which admin page is showing.
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/eligibility.php';
require_once __DIR__ . '/class-ai-launchpad-memberships.php';
require_once __DIR__ . '/class-ai-launchpad-rest.php';
require_once __DIR__ . '/class-ai-launchpad-listeners.php';
require_once __DIR__ . '/class-ai-launchpad-theme-listener.php';
require_once __DIR__ . '/class-ai-launchpad-social-listener.php';
require_once __DIR__ . '/class-ai-launchpad-subscribers-listener.php';
require_once __DIR__ . '/class-ai-launchpad-about-page-listener.php';
require_once __DIR__ . '/class-ai-launchpad-dev-enable.php';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* AI Launchpad About-page completion listener.
*
* @package automattic/jetpack-mu-wpcom
*/

/**
* Completes the About-page tasks from wp-admin when the AI Launchpad selected
* them: `add_about_page` (the AI-created About page is first published) and
* `update_about_page` (it is edited again afterwards).
*
* The catalog's own completion for these depends on the `_wpcom_template_layout_category`
* meta, which is provided by the dotcom editor toolkit and is not registered on
* Atomic, and on the AI's `createPatternPage` only ever creating a draft. So this
* tags the AI-created About page with its own marker meta and watches that page's
* publish/update transitions instead — independent of the layout-category meta.
*/
class AI_Launchpad_About_Page_Listener {

/**
* Marker meta set by createPatternPage on the AI-created About page.
*/
const META_KEY = '_wpcom_ai_launchpad_about_page';

/**
* Registers the marker meta and the publish/update watcher.
*
* @return void
*/
public static function register() {
add_action( 'init', array( __CLASS__, 'register_meta' ) );
add_action( 'transition_post_status', array( __CLASS__, 'maybe_complete' ), 10, 3 );
}

/**
* Registers the marker meta so the block editor preserves it and the create
* request can set it. Protected (underscore-prefixed), so the auth callback
* limits writes to users who can edit pages.
*
* @return void
*/
public static function register_meta() {
register_post_meta(
'page',
self::META_KEY,
array(
'type' => 'boolean',
'single' => true,
'show_in_rest' => true,
'auth_callback' => static function () {
return current_user_can( 'edit_pages' );
},
)
);
}

/**
* Completes the About-page tasks on the AI About page's status transitions:
* first publish -> add_about_page, a later edit of the published page ->
* update_about_page. Only fires for the marked page and AI-selected tasks.
*
* @param string $new_status The new post status.
* @param string $old_status The previous post status.
* @param \WP_Post $post The post being transitioned.
* @return void
*/
public static function maybe_complete( $new_status, $old_status, $post ) {
if ( 'publish' !== $new_status || ! ( $post instanceof \WP_Post ) || 'page' !== $post->post_type ) {
return;
}

$ai_task_ids = wpcom_ai_launchpad_get_ai_task_ids();
if ( empty( $ai_task_ids ) ) {
return;
}

if ( ! get_post_meta( $post->ID, self::META_KEY, true ) ) {
return;
}

if ( 'publish' !== $old_status ) {
// First publish of the AI About page.
if ( in_array( 'add_about_page', $ai_task_ids, true ) ) {
wpcom_mark_launchpad_task_complete( 'add_about_page' );
}
} elseif ( in_array( 'update_about_page', $ai_task_ids, true ) ) {
// A later edit of the already-published AI About page.
wpcom_mark_launchpad_task_complete( 'update_about_page' );
}
}
}

AI_Launchpad_About_Page_Listener::register();
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* AI Launchpad memberships completion override.
*
* @package automattic/jetpack-mu-wpcom
*
* @phan-file-suppress PhanUndeclaredClassMethod -- Jetpack_Memberships ships in the Jetpack plugin, available at runtime on Atomic; calls are guarded by class_exists.
*/

/**
* Recomputes the memberships task completion from Jetpack_Memberships' local
* signals, which the AI Launchpad REST read path uses instead of the catalog's
* own `is_complete_callback`s for these tasks.
*
* The catalog computes these from `wpcom_launchpad_get_membership_settings()`,
* which returns null under `IS_ATOMIC`, so `wpcom_launchpad_is_stripe_connected`
* / `wpcom_launchpad_has_paid_membership_plans` are always false on Atomic — and
* because those callbacks recompute (ignoring any stored option), an
* option-writing listener could not surface them. The real state is readable
* locally on Atomic, though: Jetpack_Memberships syncs the connected-account
* flag down as a site option and mirrors membership plans as the local
* `jp_mem_plan` CPT. This reads those instead.
*/
class AI_Launchpad_Memberships {

/**
* Membership tasks whose completion the AI Launchpad recomputes locally.
*/
const OVERRIDDEN_TASK_IDS = array(
'stripe_connected',
'set_up_payments',
'paid_offer_created',
'newsletter_plan_created',
);

/**
* Whether the AI Launchpad recomputes completion for the given task locally.
*
* @param string $task_id The catalog task ID.
* @return bool
*/
public static function has_override( $task_id ) {
return in_array( $task_id, self::OVERRIDDEN_TASK_IDS, true );
}

/**
* Whether an overridden membership task is complete, from Jetpack_Memberships'
* local signals. Returns false for non-overridden tasks and when the memberships
* module is unavailable.
*
* @param string $task_id The catalog task ID.
* @return bool
*/
public static function is_task_complete( $task_id ) {
if ( ! class_exists( 'Jetpack_Memberships' ) ) {
return false;
}

switch ( $task_id ) {
case 'stripe_connected':
case 'set_up_payments':
// Stripe connected = a payment method is set up; wpcom completes both
// on Stripe-connect (memberships/connected-accounts.php).
return (bool) Jetpack_Memberships::has_connected_account();
case 'paid_offer_created':
return (bool) Jetpack_Memberships::has_configured_plans_jetpack_recurring_payments();
case 'newsletter_plan_created':
return (bool) Jetpack_Memberships::has_configured_plans_jetpack_recurring_payments( 'newsletter' );
}

return false;
}
}
Loading