'ai-eshop-optimizer-pro',
'runtime' => 'ai-eshop-optimizer-runtime',
);
public static function init() {
add_action( 'admin_post_aieo_install_one', array( __CLASS__, 'handle_install' ) );
add_action( 'admin_post_aieo_one_download', array( __CLASS__, 'handle_download' ) );
add_action( 'admin_post_aieo_one_update_check', array( __CLASS__, 'handle_update_check' ) );
add_action( 'wp_ajax_aieo_one_update_check', array( __CLASS__, 'ajax_update_check' ) );
}
public static function has_key() {
if ( '' !== trim( (string) get_option( 'aieo_rooster_api_key', '' ) ) ) {
return true;
}
return defined( 'AIEO_ROOSTER_API_KEY' ) && '' !== (string) AIEO_ROOSTER_API_KEY;
}
public static function is_entitled() {
if ( ! self::has_key() ) {
return false;
}
$ent = get_option( 'aieo_rooster_entitlement' );
if ( is_array( $ent ) && isset( $ent['tier'] ) && 'free' === (string) $ent['tier'] ) {
return false;
}
if ( class_exists( 'AIEO_Entitlement_Binding' ) && AIEO_Entitlement_Binding::is_untrusted() ) {
return false;
}
return true;
}
public static function blocker() {
if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
return 'file_mods';
}
if ( ! current_user_can( 'install_plugins' ) ) {
return 'no_cap';
}
if ( is_multisite() && ! is_super_admin() ) {
return 'multisite';
}
if ( ! function_exists( 'get_filesystem_method' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
if ( 'direct' !== get_filesystem_method() ) {
return 'no_direct_fs';
}
return '';
}
public static function missing() {
$out = array();
foreach ( self::PLUGINS as $slug => $dir ) {
if ( ! file_exists( WP_PLUGIN_DIR . '/' . $dir . '/' . $dir . '.php' ) ) {
$out[ $slug ] = $dir;
}
}
return $out;
}
public static function installed() {
return array_diff_key( self::PLUGINS, self::missing() );
}
public static function download_link( $slug ) {
return wp_nonce_url(
add_query_arg(
array( 'action' => 'aieo_one_download', 'slug' => $slug ),
admin_url( 'admin-post.php' )
),
'aieo_one_download_' . $slug
);
}
public static function update_check_link() {
return wp_nonce_url(
add_query_arg( array( 'action' => 'aieo_one_update_check' ), admin_url( 'admin-post.php' ) ),
'aieo_one_update_check'
);
}
public static function render( $context = '' ) {
if ( ! self::has_key() ) {
return;
}
if ( ! current_user_can( AIEO_CAPABILITY ) ) {
return;
}
$missing = self::missing();
if ( empty( $missing ) ) {
return;
}
if ( ! self::is_entitled() ) {
echo '
'
. esc_html__( 'Your subscription has ended, so the Pro downloads are not available. The site stays connected and every free feature keeps working.', 'ai-eshop-optimizer' )
. ' '
. esc_html__( 'Renew to unlock it →', 'ai-eshop-optimizer' )
. '
';
return;
}
$names = array(
'pro' => __( 'Rooster One Pro', 'ai-eshop-optimizer' ),
'runtime' => __( 'the front-end runtime', 'ai-eshop-optimizer' ),
);
$labels = array();
foreach ( array_keys( $missing ) as $slug ) {
$labels[] = isset( $names[ $slug ] ) ? $names[ $slug ] : $slug;
}
echo '';
echo '
'
. esc_html( sprintf(
__( 'Your subscription includes %s, which is a separate download.', 'ai-eshop-optimizer' ),
implode( __( ' and ', 'ai-eshop-optimizer' ), $labels )
) )
. '
';
$blocker = self::blocker();
if ( '' === $blocker ) {
echo '
';
echo '
'
. esc_html__( 'Installs from rooster.systems using WordPress\'s own plugin installer. Nothing is activated or changed on your storefront until it finishes.', 'ai-eshop-optimizer' )
. '
';
} else {
self::render_blocked( $blocker, $missing );
}
echo '
';
}
private static function render_blocked( $blocker, array $missing ) {
switch ( $blocker ) {
case 'no_cap':
echo ''
. esc_html__( 'Your account cannot install plugins on this site, so ask an administrator to do it. They can use the button on this screen, or you can hand them the download links below.', 'ai-eshop-optimizer' )
. '
';
self::render_manual( $missing );
return;
case 'multisite':
echo ''
. esc_html__( 'This is a multisite network, so only a network administrator can install plugins. Ask them to do it, or hand them the download links below.', 'ai-eshop-optimizer' )
. '
';
self::render_manual( $missing );
return;
case 'file_mods':
echo ''
. esc_html__( 'Plugin installation is switched off on this site (DISALLOW_FILE_MODS), which is a deliberate choice by whoever set it up. Download the files below and deploy them the way you normally deploy plugins.', 'ai-eshop-optimizer' )
. '
';
self::render_manual( $missing );
return;
case 'no_direct_fs':
default:
echo ''
. esc_html__( 'Your host does not let WordPress write to the plugins folder directly, so an in-place install would only ask you for FTP details and probably fail. Download the files below and upload them under Plugins → Add New, or deploy them however you normally do.', 'ai-eshop-optimizer' )
. '
';
self::render_manual( $missing );
return;
}
}
private static function render_manual( array $missing ) {
echo '';
}
public static function ticket_url( $slug ) {
$portal = self::portal();
$key = self::key();
if ( '' === $portal || '' === $key ) {
return '';
}
$resp = wp_remote_get(
$portal . '/wp-json/om-portal/v1/one/release/' . $slug . '/ticket',
array(
'headers' => array( 'X-OM-API-Key' => $key, 'Accept' => 'application/json' ),
'timeout' => 15,
'sslverify' => (bool) apply_filters( 'aieo_portal_sslverify', true ),
)
);
if ( is_wp_error( $resp ) || 200 !== (int) wp_remote_retrieve_response_code( $resp ) ) {
return '';
}
$body = json_decode( (string) wp_remote_retrieve_body( $resp ), true );
if ( ! is_array( $body ) || empty( $body['url'] ) ) {
return '';
}
$url = (string) $body['url'];
$got = wp_parse_url( $url, PHP_URL_HOST );
$want = wp_parse_url( $portal, PHP_URL_HOST );
if ( ! $got || ! $want || 0 !== strcasecmp( (string) $got, (string) $want ) ) {
return '';
}
if ( 'https' !== strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) ) ) {
return '';
}
return $url;
}
public static function handle_download() {
if ( ! current_user_can( AIEO_CAPABILITY ) ) {
wp_die( esc_html__( 'Unauthorized', 'ai-eshop-optimizer' ) );
}
$slug = isset( $_GET['slug'] ) ? sanitize_key( wp_unslash( $_GET['slug'] ) ) : '';
if ( ! isset( self::PLUGINS[ $slug ] ) ) {
wp_die( esc_html__( 'Unknown download.', 'ai-eshop-optimizer' ) );
}
check_admin_referer( 'aieo_one_download_' . $slug );
if ( ! self::is_entitled() ) {
self::finish( array( 'ok' => false, 'reason' => 'not_entitled' ) );
}
$url = self::ticket_url( $slug );
if ( '' === $url ) {
self::finish( array( 'ok' => false, 'reason' => 'no_ticket', 'slug' => $slug ) );
}
wp_redirect( $url );
exit;
}
private static function run_update_check() {
$found = array();
if ( class_exists( 'AIEO_One_Updater' ) ) {
foreach ( array_keys( self::PLUGINS ) as $slug ) {
$remote = AIEO_One_Updater::refresh( $slug );
if ( is_array( $remote ) && ! empty( $remote['version'] ) ) {
$found[ $slug ] = (string) $remote['version'];
}
}
}
delete_site_transient( 'update_plugins' );
return $found;
}
public static function handle_update_check() {
if ( ! current_user_can( AIEO_CAPABILITY ) ) {
wp_die( esc_html__( 'Unauthorized', 'ai-eshop-optimizer' ) );
}
check_admin_referer( 'aieo_one_update_check' );
self::finish( array( 'ok' => true, 'action' => 'update_check', 'found' => self::run_update_check() ) );
}
public static function ajax_update_check() {
if ( ! current_user_can( AIEO_CAPABILITY ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', 'ai-eshop-optimizer' ) ), 403 );
}
check_ajax_referer( 'aieo_one_update_check', 'nonce' );
$found = self::run_update_check();
$core = defined( 'AIEO_VERSION' ) ? (string) AIEO_VERSION : '';
$lines = array();
$updates = 0;
$unreachable = 0;
$names = array(
'pro' => __( 'Pro plugin', 'ai-eshop-optimizer' ),
'runtime' => __( 'front-end runtime', 'ai-eshop-optimizer' ),
);
foreach ( array_keys( self::PLUGINS ) as $slug ) {
$name = isset( $names[ $slug ] ) ? $names[ $slug ] : $slug;
if ( empty( $found[ $slug ] ) ) {
$unreachable++;
$lines[] = sprintf( __( '%s: no answer from rooster.systems', 'ai-eshop-optimizer' ), $name );
continue;
}
$remote = $found[ $slug ];
if ( '' !== $core && version_compare( $remote, $core, '>' ) ) {
$updates++;
$lines[] = sprintf( __( '%1$s: %2$s available', 'ai-eshop-optimizer' ), $name, $remote );
} else {
$lines[] = sprintf( __( '%s: up to date', 'ai-eshop-optimizer' ), $name );
}
}
if ( $updates > 0 ) {
$summary = sprintf( _n( '%d update available.', '%d updates available.', $updates, 'ai-eshop-optimizer' ), $updates );
} elseif ( $unreachable > 0 ) {
$summary = __( 'Could not complete the check — nothing was confirmed.', 'ai-eshop-optimizer' );
} else {
$summary = __( 'Everything is up to date.', 'ai-eshop-optimizer' );
}
wp_send_json_success(
array(
'updates' => $updates,
'unreachable' => $unreachable,
'lines' => $lines,
'summary' => $summary,
)
);
}
public static function handle_install() {
if ( ! current_user_can( AIEO_CAPABILITY ) || ! current_user_can( 'install_plugins' ) ) {
wp_die( esc_html__( 'Unauthorized', 'ai-eshop-optimizer' ) );
}
check_admin_referer( 'aieo_install_one', 'aieo_install_one_nonce' );
$blocker = self::blocker();
if ( '' !== $blocker ) {
self::finish( array( 'ok' => false, 'blocked' => $blocker ) );
}
$missing = self::missing();
if ( empty( $missing ) ) {
self::finish( array( 'ok' => true, 'installed' => array(), 'already' => true ) );
}
if ( ! self::is_entitled() ) {
self::finish( array( 'ok' => false, 'reason' => 'not_entitled' ) );
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/misc.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';
$portal = self::portal();
$key = self::key();
$prefixes = array();
foreach ( array_keys( $missing ) as $slug ) {
$prefixes[] = $portal . '/wp-json/om-portal/v1/one/release/' . $slug . '/download';
}
$inject = static function ( $args, $url ) use ( $prefixes, $key ) {
foreach ( $prefixes as $prefix ) {
if ( is_string( $url ) && 0 === strpos( $url, $prefix ) ) {
if ( ! isset( $args['headers'] ) || ! is_array( $args['headers'] ) ) {
$args['headers'] = array();
}
$args['headers']['X-OM-API-Key'] = $key;
if ( ! isset( $args['redirection'] ) || (int) $args['redirection'] < 3 ) {
$args['redirection'] = 3;
}
break;
}
}
return $args;
};
add_filter( 'http_request_args', $inject, 10, 2 );
$installed = array();
$failed = array();
try {
$skin = new Automatic_Upgrader_Skin();
$upgrader = new Plugin_Upgrader( $skin );
foreach ( $missing as $slug => $dir ) {
$package = $portal . '/wp-json/om-portal/v1/one/release/' . $slug . '/download';
$result = $upgrader->install( $package, array( 'overwrite_package' => false ) );
if ( is_wp_error( $result ) ) {
$failed[ $dir ] = $result->get_error_message();
continue;
}
if ( true !== $result ) {
$messages = $skin->get_upgrade_messages();
$failed[ $dir ] = is_array( $messages ) && ! empty( $messages )
? (string) end( $messages )
: __( 'The installer did not report a reason.', 'ai-eshop-optimizer' );
continue;
}
$installed[] = $dir;
}
} catch ( \Throwable $e ) {
$failed['__exception'] = substr( $e->getMessage(), 0, 300 );
} finally {
remove_filter( 'http_request_args', $inject, 10 );
}
$activated = array();
if ( empty( $failed ) ) {
foreach ( $installed as $dir ) {
$basename = $dir . '/' . $dir . '.php';
if ( ! is_plugin_active( $basename ) ) {
$err = activate_plugin( $basename );
if ( is_wp_error( $err ) ) {
$failed[ $dir ] = $err->get_error_message();
continue;
}
}
$activated[] = $dir;
}
}
if ( class_exists( 'AIEO_Runtime_Presence' ) ) {
AIEO_Runtime_Presence::flush();
}
self::finish( array(
'ok' => empty( $failed ),
'installed' => $installed,
'activated' => $activated,
'failed' => $failed,
) );
}
private static function finish( array $result ) {
$result['at'] = time();
update_option( self::OPT_RESULT, $result, false );
wp_safe_redirect( wp_get_referer() ?: admin_url( 'admin.php?page=ai-eshop-optimizer' ) );
exit;
}
public static function render_result() {
$r = get_option( self::OPT_RESULT );
if ( ! is_array( $r ) || empty( $r ) ) {
return;
}
delete_option( self::OPT_RESULT );
if ( isset( $r['action'] ) && 'update_check' === $r['action'] ) {
$found = isset( $r['found'] ) ? (array) $r['found'] : array();
$names = array(
'pro' => __( 'Rooster One Pro', 'ai-eshop-optimizer' ),
'runtime' => __( 'the front-end runtime', 'ai-eshop-optimizer' ),
);
$parts = array();
foreach ( $found as $slug => $version ) {
$parts[] = ( isset( $names[ $slug ] ) ? $names[ $slug ] : $slug ) . ' ' . sanitize_text_field( (string) $version );
}
echo '';
if ( $parts ) {
echo esc_html( sprintf(
__( 'Checked with rooster.systems. Latest available: %s. Anything newer than what you have is now on Dashboard > Updates.', 'ai-eshop-optimizer' ),
implode( ', ', $parts )
) );
} else {
echo esc_html__( 'Could not reach rooster.systems for a version just now. Nothing changed. Try again shortly.', 'ai-eshop-optimizer' );
}
echo '
';
return;
}
if ( isset( $r['reason'] ) && 'no_ticket' === $r['reason'] ) {
echo ''
. esc_html__( 'Could not reach rooster.systems for a download link just now. Nothing changed. Try again shortly.', 'ai-eshop-optimizer' )
. '
';
return;
}
if ( ! empty( $r['ok'] ) ) {
$dirs = ! empty( $r['activated'] ) ? $r['activated'] : ( isset( $r['installed'] ) ? $r['installed'] : array() );
if ( empty( $dirs ) ) {
return;
}
echo ''
. esc_html( sprintf(
__( 'Installed and activated: %s. The screens that needed them are live now, with no further setup.', 'ai-eshop-optimizer' ),
implode( ', ', array_map( 'sanitize_text_field', $dirs ) )
) )
. '
';
return;
}
if ( ! empty( $r['blocked'] ) ) {
echo ''
. esc_html__( 'That install could not run on this host. Reload the page and use the download links shown there instead.', 'ai-eshop-optimizer' )
. '
';
return;
}
if ( isset( $r['reason'] ) && 'not_entitled' === $r['reason'] ) {
echo ''
. esc_html__( 'rooster.systems declined the download for this site. That usually means the subscription has ended or every purchased seat is in use.', 'ai-eshop-optimizer' )
. '
';
return;
}
echo ''
. esc_html__( 'The install did not complete.', 'ai-eshop-optimizer' )
. '
';
foreach ( (array) ( isset( $r['failed'] ) ? $r['failed'] : array() ) as $dir => $msg ) {
echo '- ' . esc_html( (string) $dir . ' — ' . (string) $msg ) . '
';
}
echo '
'
. esc_html__( 'Nothing was activated, so your site is unchanged. Reload this screen to download the files and install them by hand.', 'ai-eshop-optimizer' )
. '
';
}
private static function portal() {
if ( class_exists( 'AIEO_Rooster_Signup' ) && is_callable( array( 'AIEO_Rooster_Signup', 'portal_url' ) ) ) {
return rtrim( (string) AIEO_Rooster_Signup::portal_url(), '/' );
}
$url = trim( (string) get_option( 'aieo_rooster_hub_url', 'https://rooster.systems' ) );
return rtrim( '' !== $url ? $url : 'https://rooster.systems', '/' );
}
private static function key() {
$key = trim( (string) get_option( 'aieo_rooster_api_key', '' ) );
if ( '' !== $key ) {
return $key;
}
return ( defined( 'AIEO_ROOSTER_API_KEY' ) && '' !== (string) AIEO_ROOSTER_API_KEY )
? trim( (string) AIEO_ROOSTER_API_KEY )
: '';
}
}