__( 'Facebook', 'ai-eshop-optimizer' ), 'instagram' => __( 'Instagram', 'ai-eshop-optimizer' ), 'x' => __( 'X (Twitter)', 'ai-eshop-optimizer' ), 'linkedin' => __( 'LinkedIn', 'ai-eshop-optimizer' ), 'tiktok' => __( 'TikTok', 'ai-eshop-optimizer' ), ); return isset( $labels[ $platform ] ) ? $labels[ $platform ] : ucfirst( $platform ); } private function app_platforms() { return array_values( array_diff( AIEO_Social_Store::PLATFORMS, array( 'instagram' ) ) ); } public function get_id() { return 'social'; } public function get_icon() { return 'send'; } public function get_label() { return __( 'Social posts', 'ai-eshop-optimizer' ); } public function get_group() { return 'promote'; } public function get_section() { return 'create_publish'; } public function get_priority() { return 55; } public function is_pro_locked() { return true; } public function get_pro_promo() { return array( 'title' => __( 'Rooster One Social', 'ai-eshop-optimizer' ), 'description' => __( 'Turn your catalogue, offers and reviews into on-brand social posts and publish them straight to your own Facebook, Instagram, X, LinkedIn and TikTok — from inside Rooster One. Your shop runs its OWN platform apps and holds its OWN tokens, so nothing routes through a third party. The AI writer drafts platform-native variants; you approve; and engagement flows back into the same 360 customer-profiling loop that powers your audiences, offers and recommendations.', 'ai-eshop-optimizer' ), 'bullets' => array( __( 'AI-written posts tailored per platform — FB / IG / X / LinkedIn / TikTok', 'ai-eshop-optimizer' ), __( 'Site-owned apps & tokens, encrypted at rest — no portal brokering', 'ai-eshop-optimizer' ), __( 'Publish to your own Pages and profiles with one-click approvals', 'ai-eshop-optimizer' ), __( 'Reactions and clicks feed the 360 loop — audiences, offers, recos', 'ai-eshop-optimizer' ), ), ); } public function register_hooks() { if (is_admin()) { add_action('admin_enqueue_scripts', function () { if ((isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '') === 'ai-eshop-dmm' && (isset($_GET['dmm_tab']) ? sanitize_text_field(wp_unslash($_GET['dmm_tab'])) : '') === 'social') { $css = 'assets/css/aieo-social-admin.css'; wp_enqueue_style('aieo-social-admin', AIEO_URI . $css, array(), file_exists(AIEO_DIR . $css) ? (string) filemtime(AIEO_DIR . $css) : '1'); } }); } add_action( 'admin_post_' . self::NONCE_SAVE, array( $this, 'handle_save_app' ) ); add_action( 'admin_post_' . self::NONCE_DISCONNECT, array( $this, 'handle_disconnect_account' ) ); } public function handle_save_app() { if ( ! current_user_can( AIEO_CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to do this.', 'ai-eshop-optimizer' ), 403 ); } check_admin_referer( self::NONCE_SAVE ); $platform = isset( $_POST['platform'] ) ? sanitize_key( wp_unslash( $_POST['platform'] ) ) : ''; if ( ! in_array( $platform, $this->app_platforms(), true ) ) { $this->redirect_back( 'error', __( 'Unknown platform.', 'ai-eshop-optimizer' ) ); } $app_id = isset( $_POST['app_id'] ) ? sanitize_text_field( wp_unslash( $_POST['app_id'] ) ) : ''; $app_secret = isset( $_POST['app_secret'] ) ? sanitize_text_field( wp_unslash( $_POST['app_secret'] ) ) : ''; if ( '' === $app_secret ) { $existing = AIEO_Social_Store::get_app( $platform ); if ( ! empty( $existing['app_secret'] ) ) { $app_secret = (string) $existing['app_secret']; } } AIEO_Social_Store::save_app( $platform, $app_id, $app_secret ); $this->redirect_back( 'success', sprintf( __( '%s app saved.', 'ai-eshop-optimizer' ), $this->platform_label( $platform ) ) ); } public function handle_disconnect_account() { if ( ! current_user_can( AIEO_CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to do this.', 'ai-eshop-optimizer' ), 403 ); } check_admin_referer( self::NONCE_DISCONNECT ); $account_id = isset( $_POST['account_id'] ) ? absint( wp_unslash( $_POST['account_id'] ) ) : 0; if ( $account_id > 0 ) { AIEO_Social_Store::delete_account( $account_id ); } $this->redirect_back( 'success', __( 'Account disconnected.', 'ai-eshop-optimizer' ) ); } private function redirect_back( $type, $message ) { $url = add_query_arg( array( 'aieo_social_notice' => rawurlencode( $type ), 'aieo_social_msg' => rawurlencode( $message ), ), $this->get_url() ); wp_safe_redirect( $url ); exit; } public function render_subtab() { list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-posts', 'icon' => 'send', 'title' => __( 'How your posts get published', 'ai-eshop-optimizer' ), 'description' => __( 'Your own social publishing, run from your own platform apps. You register the app, the tokens stay on this site encrypted, and posts publish straight to your Pages and profiles — nothing is brokered through us. Below: what the nightly ranking suggests promoting, the apps and accounts that do the publishing, and what each platform requires before it will let you post.', 'ai-eshop-optimizer' ), ) ); echo $open . $this->notice_html(); $this->render_jump_links(); echo $close; $this->render_slate_section(); $this->render_restock_section(); $this->render_apps_section(); $this->render_accounts_section(); $this->render_approvals_section(); $this->print_js(); } private function render_jump_links() { $links = array( '#aieo-card-social-boost-slate' => __( 'Slate', 'ai-eshop-optimizer' ), '#aieo-card-social-restock-to-unlock' => __( 'Restock to unlock', 'ai-eshop-optimizer' ), '#aieo-card-social-platform-apps' => __( 'Platform apps', 'ai-eshop-optimizer' ), '#aieo-card-social-accounts' => __( 'Accounts', 'ai-eshop-optimizer' ), '#aieo-card-social-approvals' => __( 'Approvals', 'ai-eshop-optimizer' ), ); $parts = array(); foreach ( $links as $href => $label ) { $parts[] = '' . esc_html( $label ) . ''; } echo '

' . esc_html__( 'Jump to:', 'ai-eshop-optimizer' ) . ' ' . implode( '  ·  ', $parts ) . '

'; } private function render_slate_section() { global $wpdb; $t = $wpdb->prefix . 'aieo_boost_candidates'; $rows = array(); $empty = __( 'The slate builds overnight once Social is active.', 'ai-eshop-optimizer' ); if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) === $t ) { $rows = $wpdb->get_results( "SELECT object_type, object_id, boost_score, seo_quadrant, eligible_vectors, veto_reasons FROM `{$t}` WHERE boost_score > 0 ORDER BY boost_score DESC LIMIT 20", ARRAY_A ); $empty = __( 'No candidates scored yet — the slate rebuilds nightly at 03:50.', 'ai-eshop-optimizer' ); } list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-boost-slate', 'icon' => 'rocket', 'title' => __( 'Boost slate', 'ai-eshop-optimizer' ), 'description' => __( 'The nightly fusion ranks every post, page and product by the intelligence signals you already collect (SEO demand, sales forecast, on-site intent, push appetite) and recommends what to promote and where. Accept one to open it pre-wired in the editor.', 'ai-eshop-optimizer' ), 'flush' => ! empty( $rows ), ) ); echo $open; if ( ! $rows ) { echo \Rooster\Admin\States::state( 'not-run', array( 'title' => $empty ) ); echo $close; return; } $slate = array(); foreach ( $rows as $r ) { $oid = (int) $r['object_id']; $title = get_the_title( $oid ); if ( '' === $title ) { $title = ucfirst( (string) $r['object_type'] ) . ' #' . $oid; } $slate[] = array( 'oid' => $oid, 'title' => $title, 'type' => (string) $r['object_type'], 'score' => (float) $r['boost_score'], 'signal' => (string) $r['seo_quadrant'], 'channels' => $this->slate_channel_cell( (string) $r['eligible_vectors'], (string) $r['veto_reasons'] ), 'edit' => (string) get_edit_post_link( $oid ), ); } AIEO_Table::render( array( 'page_id' => $this->get_id(), 'row_key' => 'oid', 'rows' => $slate, 'caps' => array( 'search' => true ), 'cap' => 20, 'columns' => array( array( 'key' => 'title', 'label' => __( 'What to promote', 'ai-eshop-optimizer' ), 'suffix' => 'type' ), array( 'key' => 'score', 'label' => __( 'Score', 'ai-eshop-optimizer' ), 'type' => 'decimal', 'format' => array( 'precision' => 1 ) ), array( 'key' => 'signal', 'label' => __( 'Signal', 'ai-eshop-optimizer' ) ), array( 'key' => 'channels', 'label' => __( 'Suggested channels', 'ai-eshop-optimizer' ), 'type' => 'html', 'sortable' => false ), ), 'actions' => array( array( 'id' => 'open', 'label' => __( 'Open', 'ai-eshop-optimizer' ), 'description' => __( 'Opens the product or post in its editor.', 'ai-eshop-optimizer' ), 'visible' => static function ( $r ) { return '' !== $r['edit']; }, 'href' => static function ( $r ) { return $r['edit']; }, ), ), ) ); echo $close; } private function slate_channel_cell( $vectors, $veto ) { if ( '' !== $vectors ) { return esc_html( $vectors ); } if ( '' !== $veto && class_exists( 'AIEO_Boost_Candidates' ) ) { $split = AIEO_Boost_Candidates::split_vetoes( $veto ); if ( ! empty( $split['permanent'] ) ) { return '' . esc_html__( 'Held back — customer ratings', 'ai-eshop-optimizer' ) . ''; } if ( ! empty( $split['transient'] ) ) { return \Rooster\Admin\Ui::badge( __( 'Waiting on stock', 'ai-eshop-optimizer' ), 'warning' ); } } return '' . esc_html__( 'connect a channel', 'ai-eshop-optimizer' ) . ''; } private function render_restock_section() { global $wpdb; $w = $wpdb->prefix . 'aieo_waitlist'; if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $w ) ) !== $w ) { return; } $rows = $wpdb->get_results( "SELECT CASE WHEN p.post_type = 'product_variation' AND p.post_parent > 0 THEN p.post_parent ELSE p.ID END AS pid, COUNT(DISTINCT w.email) AS waiting FROM `{$w}` w INNER JOIN `{$wpdb->posts}` p ON p.ID = w.product_id AND p.post_status = 'publish' WHERE w.notified_at IS NULL GROUP BY pid ORDER BY waiting DESC LIMIT 25", ARRAY_A ); if ( ! $rows ) { return; } $blocked = array(); $total = 0; foreach ( $rows as $r ) { $pid = (int) $r['pid']; if ( 'instock' === (string) get_post_meta( $pid, '_stock_status', true ) ) { continue; } $blocked[] = array( 'pid' => $pid, 'waiting' => (int) $r['waiting'] ); $total += (int) $r['waiting']; } if ( ! $blocked ) { return; } list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-restock-to-unlock', 'icon' => 'boxes', 'title' => __( 'Restock to unlock', 'ai-eshop-optimizer' ), 'description' => __( 'Lists the out-of-stock products people asked to be notified about and how many are waiting, so you can restock them and unlock them for promotion.', 'ai-eshop-optimizer' ), 'tools' => \Rooster\Admin\Ui::badge( sprintf( _n( '%1$s person waiting across %2$s product', '%1$s people waiting across %2$s products', $total, 'ai-eshop-optimizer' ), number_format_i18n( $total ), number_format_i18n( count( $blocked ) ) ), 'warning' ), ) ); echo $open; echo \Rooster\Admin\Guidance::text( __( 'These products are out of stock and people have asked to be told when they return. That is the strongest demand signal in the shop — it was volunteered, not inferred. Promotion is held back while they cannot be bought; restock one and it moves straight onto the slate with an audience already waiting.', 'ai-eshop-optimizer' ) ); $restock = array(); foreach ( $blocked as $b ) { $title = get_the_title( $b['pid'] ); if ( '' === $title ) { $title = 'Product #' . $b['pid']; } $restock[] = array( 'pid' => (int) $b['pid'], 'title' => $title, 'waiting' => (int) $b['waiting'], 'edit' => (string) get_edit_post_link( $b['pid'] ), ); } AIEO_Table::render( array( 'page_id' => $this->get_id(), 'row_key' => 'pid', 'rows' => $restock, 'caps' => array( 'search' => true ), 'columns' => array( array( 'key' => 'title', 'label' => __( 'Product', 'ai-eshop-optimizer' ) ), array( 'key' => 'waiting', 'label' => __( 'People waiting', 'ai-eshop-optimizer' ), 'type' => 'integer' ), ), 'actions' => array( array( 'id' => 'restock', 'label' => __( 'Restock', 'ai-eshop-optimizer' ), 'visible' => static function ( $r ) { return '' !== $r['edit']; }, 'href' => static function ( $r ) { return $r['edit']; }, ), ), ) ); echo $close; } private function render_apps_section() { $band = function_exists( 'rooster_admin_band_open' ); if ( $band ) { rooster_admin_band_open( array( 'cols' => 2 ) ); } list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-platform-apps', 'icon' => 'plug', 'title' => __( 'Platform apps', 'ai-eshop-optimizer' ), 'description' => __( 'Each platform app is yours, exactly like a Facebook SSO app: create the app in the platform\'s developer portal, paste its App ID and App Secret here, and set the OAuth redirect URI below inside that app. Rooster One then runs the login flow with YOUR app and stores the resulting tokens site-side, encrypted — no keys ever leave your shop.', 'ai-eshop-optimizer' ), ) ); echo $open . $close; $save_url = $this->get_form_action_url( self::NONCE_SAVE ); foreach ( $this->app_platforms() as $platform ) { $this->app_panel( $platform, $save_url ); } if ( $band ) { rooster_admin_band_close(); } } private function app_panel( $platform, $save_url ) { $app = AIEO_Social_Store::get_app( $platform ); $configured = AIEO_Social_Store::app_configured( $platform ); $app_id = isset( $app['app_id'] ) ? (string) $app['app_id'] : ''; $has_secret = ! empty( $app['app_secret'] ); $redirect = AIEO_Social_Store::redirect_uri( $platform ); $anchors = array( 'facebook' => 'social-facebook', 'x' => 'social-x-twitter', 'linkedin' => 'social-linkedin', 'tiktok' => 'social-tiktok', ); $form_id = 'aieo-social-app-form-' . sanitize_html_class( $platform ); $descriptions = array( 'facebook' => __( 'Your Meta app from developers.facebook.com: it connects your Facebook Pages and the Instagram Business accounts linked to them.', 'ai-eshop-optimizer' ), 'x' => __( 'Your X app from developer.x.com: its OAuth 2.0 Client ID and Client Secret, for posts to your own X account.', 'ai-eshop-optimizer' ), 'linkedin' => __( 'Your LinkedIn app from linkedin.com/developers: its Client ID and Client Secret, for posts to the company Pages you administer.', 'ai-eshop-optimizer' ), 'tiktok' => __( 'Your TikTok app from developers.tiktok.com: its Client key goes in App ID and its Client secret in App Secret.', 'ai-eshop-optimizer' ), ); list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => isset( $anchors[ $platform ] ) ? $anchors[ $platform ] : 'social-' . sanitize_html_class( $platform ), 'icon' => 'key', 'title' => $this->platform_label( $platform ), 'description' => isset( $descriptions[ $platform ] ) ? $descriptions[ $platform ] : __( 'Enter the App ID and App Secret from the platform\'s developer portal, then paste the read-only redirect URI into that app\'s OAuth settings.', 'ai-eshop-optimizer' ), 'tools' => \Rooster\Admin\Ui::badge( $configured ? __( 'Platform app ID and secret', 'ai-eshop-optimizer' ) : __( 'not set', 'ai-eshop-optimizer' ), $configured ? 'success' : 'mono' ), 'footer' => '', ) ); echo $open; echo '
'; wp_nonce_field( self::NONCE_SAVE ); echo ''; $pid = sanitize_html_class( $platform ); $rows = \Rooster\Admin\Ui::field_row( array( 'id' => 'aieo-social-appid-' . $pid, 'label' => __( 'App ID', 'ai-eshop-optimizer' ), 'control' => \Rooster\Admin\Ui::input( 'app_id', $app_id, array( 'id' => 'aieo-social-appid-' . $pid, 'medium' => true, 'code' => true, 'autocomplete' => 'off', 'attrs' => array( 'spellcheck' => 'false' ) ) ), ) ) . \Rooster\Admin\Ui::field_row( array( 'id' => 'aieo-social-secret-' . $pid, 'label' => __( 'App Secret', 'ai-eshop-optimizer' ), 'control' => \Rooster\Admin\Fields::secret( 'app_secret', $has_secret, array( 'id' => 'aieo-social-secret-' . $pid, 'label' => __( 'App Secret', 'ai-eshop-optimizer' ), 'placeholder' => __( 'Paste the app secret', 'ai-eshop-optimizer' ), ) ), ) ) . \Rooster\Admin\Ui::field_row( array( 'id' => 'aieo-social-redirect-' . $pid, 'label' => __( 'OAuth redirect URI', 'ai-eshop-optimizer' ), 'control' => '' . '' . '', 'help' => __( 'Paste this exact URI into your app\'s Valid OAuth redirect URIs / callback settings.', 'ai-eshop-optimizer' ), ) ); echo '
' . $rows . '
'; echo '
'; echo $close; } private function render_accounts_section() { $start_endpoint = esc_url_raw( rest_url( 'aieo/v1/social/oauth/start' ) ); $rest_nonce = wp_create_nonce( 'wp_rest' ); list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-accounts', 'icon' => 'users', 'title' => __( 'Accounts', 'ai-eshop-optimizer' ), 'description' => __( 'Connect a Page or profile per platform. Connect launches the login flow in a popup using your own app; when it finishes, the connected accounts appear below with their token status. Connect is enabled only for platforms whose app is configured above.', 'ai-eshop-optimizer' ), ) ); echo $open; echo '
'; $any_off = false; foreach ( $this->app_platforms() as $platform ) { $configured = AIEO_Social_Store::app_configured( $platform ); $any_off = $any_off || ! $configured; echo ''; } echo ''; echo '
'; if ( $any_off ) { echo '

' . esc_html__( 'Connect is enabled only for platforms whose app is configured above.', 'ai-eshop-optimizer' ) . '

'; } $accounts = AIEO_Social_Store::get_accounts(); $disc_url = $this->get_form_action_url( self::NONCE_DISCONNECT ); if ( empty( $accounts ) ) { echo \Rooster\Admin\States::state( 'first-use', array( 'title' => __( 'No accounts connected yet.', 'ai-eshop-optimizer' ) ) ); } else { $table_rows = array(); foreach ( $accounts as $acct ) { $id = isset( $acct['id'] ) ? (int) $acct['id'] : 0; $name = isset( $acct['name'] ) ? (string) $acct['name'] : ''; $plat = isset( $acct['platform'] ) ? (string) $acct['platform'] : ''; $status = isset( $acct['token_status'] ) ? (string) $acct['token_status'] : ''; $form = '
' . wp_nonce_field( self::NONCE_DISCONNECT, '_wpnonce', true, false ) . '' . \Rooster\Admin\Ui::button( __( 'Disconnect', 'ai-eshop-optimizer' ), array( 'size' => 'sm', 'type' => 'submit', 'class' => 'is-destructive', 'attrs' => array( 'aria-label' => sprintf( __( 'Disconnect %s', 'ai-eshop-optimizer' ), $name ) ) + \Rooster\Admin\States::confirm_attrs( 'aieo-social-disconnect' ) ) ) . '
'; $table_rows[] = array( '_id' => (string) $id, '_name' => $name, '_s' => array( 'name' => $name, 'platform' => $this->platform_label( $plat ), 'token' => $status ), 'name' => $name, 'platform' => $this->platform_label( $plat ), 'token' => array( 'html' => $this->token_pill( $status ) ), 'actions' => '' . $form . '', ); } echo AIEO_Table::local( 'aieo-social-accounts', array( array( 'key' => 'name', 'label' => __( 'Name', 'ai-eshop-optimizer' ), 'type' => 'text', 'sort_type' => 'text' ), array( 'key' => 'platform', 'label' => __( 'Platform', 'ai-eshop-optimizer' ), 'type' => 'text', 'sort_type' => 'text' ), array( 'key' => 'token', 'label' => __( 'Token', 'ai-eshop-optimizer' ), 'type' => 'status', 'sort_type' => 'text' ), array( 'key' => 'actions', 'label' => __( 'Actions', 'ai-eshop-optimizer' ), 'type' => 'actions' ), ), $table_rows, array( 'size' => 0, 'label' => __( 'Connected social accounts', 'ai-eshop-optimizer' ), ) ); $dialog = \Rooster\Admin\States::confirm( 'aieo-social-disconnect', 'off', __( 'Disconnect', 'ai-eshop-optimizer' ), esc_html__( 'Disconnect this account? Tokens will be removed from this site.', 'ai-eshop-optimizer' ), array( 'confirm' => __( 'Disconnect', 'ai-eshop-optimizer' ) ) ); echo $dialog; } echo $close; } private function token_pill( $status ) { $map = array( 'active' => array( 'success', __( 'Active', 'ai-eshop-optimizer' ) ), 'valid' => array( 'success', __( 'Active', 'ai-eshop-optimizer' ) ), 'expired' => array( 'mono', __( 'Expired', 'ai-eshop-optimizer' ) ), 'error' => array( 'mono', __( 'Error', 'ai-eshop-optimizer' ) ), 'revoked' => array( 'mono', __( 'Revoked', 'ai-eshop-optimizer' ) ), 'not_connected' => array( 'mono', __( 'Not connected', 'ai-eshop-optimizer' ) ), ); $key = strtolower( (string) $status ); $tone = isset( $map[ $key ] ) ? $map[ $key ][0] : 'warning'; $label = isset( $map[ $key ] ) ? $map[ $key ][1] : ( '' !== $status ? $status : __( 'Unknown', 'ai-eshop-optimizer' ) ); return \Rooster\Admin\Ui::badge( $label, $tone, array( 'sm' => true ) ); } private function render_approvals_section() { list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'social-approvals', 'icon' => 'circle-check', 'title' => __( 'Approvals', 'ai-eshop-optimizer' ), 'description' => __( 'Because each app is yours, what you need to go live differs per platform. Facebook and X publish to your own pages / account the same day with no app review. LinkedIn and TikTok require your shop\'s own access grant or app audit before publishing works.', 'ai-eshop-optimizer' ), ) ); echo $open; $rows = array( array( 'label' => __( 'Facebook', 'ai-eshop-optimizer' ), 'tier' => 'ok', 'note' => __( 'Same-day. Publishes to your own Pages with no app review.', 'ai-eshop-optimizer' ), 'portal' => 'https://developers.facebook.com/apps', 'plabel' => __( 'developers.facebook.com/apps', 'ai-eshop-optimizer' ), ), array( 'label' => __( 'X (Twitter)', 'ai-eshop-optimizer' ), 'tier' => 'ok', 'note' => __( 'Same-day. Posts to your own account with no review.', 'ai-eshop-optimizer' ), 'portal' => 'https://developer.x.com', 'plabel' => __( 'developer.x.com', 'ai-eshop-optimizer' ), ), array( 'label' => __( 'LinkedIn', 'ai-eshop-optimizer' ), 'tier' => 'review', 'note' => __( 'Needs your own access grant — request the Community Management / posting products for your app.', 'ai-eshop-optimizer' ), 'portal' => 'https://www.linkedin.com/developers', 'plabel' => __( 'linkedin.com/developers', 'ai-eshop-optimizer' ), ), array( 'label' => __( 'TikTok', 'ai-eshop-optimizer' ), 'tier' => 'review', 'note' => __( 'Needs your own app audit — apply for Content Posting access before publishing.', 'ai-eshop-optimizer' ), 'portal' => 'https://developers.tiktok.com', 'plabel' => __( 'developers.tiktok.com', 'ai-eshop-optimizer' ), ), array( 'label' => __( 'Instagram', 'ai-eshop-optimizer' ), 'tier' => 'review', 'note' => __( 'Publishes via your Facebook app + a linked Instagram Business account.', 'ai-eshop-optimizer' ), 'portal' => 'https://developers.facebook.com/apps', 'plabel' => __( 'developers.facebook.com/apps', 'ai-eshop-optimizer' ), ), ); echo ''; echo $close; } private function notice_html() { if ( empty( $_GET['aieo_social_notice'] ) ) { return ''; } $type = sanitize_key( wp_unslash( $_GET['aieo_social_notice'] ) ); $msg = isset( $_GET['aieo_social_msg'] ) ? sanitize_text_field( wp_unslash( $_GET['aieo_social_msg'] ) ) : ''; if ( '' === $msg ) { return ''; } return \Rooster\Admin\Ui::notice( esc_html( $msg ), 'error' === $type ? 'destructive' : 'success', array( 'inline' => true, 'dismissible' => true ) ); } private function print_js() { ?>