maybe_set_first_install(); // Admin hooks. add_action( 'admin_notices', [ $this, 'render_welcome_notice' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_notice_scripts' ] ); add_action( 'wp_ajax_ctc_dismiss_welcome_notice', [ $this, 'ajax_dismiss_notice' ] ); // Hook into updater for upgrade notices. add_action( 'ctc/updater/after', [ $this, 'on_version_update' ], 10, 2 ); } /** * Set first install version if not set. * * @since 5.0.0 */ private function maybe_set_first_install() { if ( ! get_option( self::FIRST_INSTALL_OPTION ) ) { update_option( self::FIRST_INSTALL_OPTION, CTC_VER ); } } /** * Check if this is a fresh install (no previous version). * * @return bool */ public function is_fresh_install() { $saved_version = get_option( Updater::VERSION_OPTION, '' ); return empty( $saved_version ) || '0.0.0' === $saved_version; } /** * Check if user upgraded from a version before 5.0.0. * * @return bool */ public function is_upgrade_to_5() { $first_install = get_option( self::FIRST_INSTALL_OPTION, '' ); return ! empty( $first_install ) && version_compare( $first_install, '5.0.0', '<' ); } /** * Check if welcome notice should be shown. * * @return bool */ public function should_show_notice() { // Don't show if dismissed. if ( get_option( self::NOTICE_DISMISSED_OPTION ) ) { return false; } // Don't show on the Global Injector page. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['page'] ) && 'ctc-global-injector' === $_GET['page'] ) { return false; } // Only show to users who can manage options. if ( ! current_user_can( 'manage_options' ) ) { return false; } return true; } /** * Get the welcome message based on install type. * * @return array{title: string, message: string, type: string} */ private function get_welcome_content() { if ( $this->is_fresh_install() ) { return [ 'title' => __( 'Copy Anything to Clipboard is ready', 'ctc' ), 'message' => __( 'Add copy buttons to code blocks, tables, and any content with Global Injector. Optionally help improve the plugin by sharing anonymous usage data from this notice or Settings → Dashboard.', 'ctc' ), 'type' => 'fresh', ]; } // Upgrade message (aligned with 5.4.1 changelog). return [ 'title' => __( 'Copy Anything to Clipboard updated', 'ctc' ), 'message' => __( 'Telemetry opt-in is now available from this notice and Settings → Dashboard. Pro: copy-by-source analytics and telemetry summary. Includes uninstall cleanup and a fix for Freemius loading.', 'ctc' ), 'type' => 'upgrade', ]; } /** * Render the welcome admin notice. * * @since 5.0.0 */ public function render_welcome_notice() { if ( ! $this->should_show_notice() ) { return; } $content = $this->get_welcome_content(); $global_injector_url = admin_url( 'admin.php?page=ctc-global-injector' ); $docs_url = 'https://docs.clipboard.agency/getting-started'; ?>

' . esc_html__( 'privacy note', 'ctc' ) . '' ); ?>

→

should_show_notice() ) { return; } // Inline styles for the notice. $styles = ' #ctc-welcome-notice { padding: 12px 12px 12px 0; border-left-color: #2271b1; } #ctc-welcome-notice .ctc-welcome-notice-content { display: flex; align-items: flex-start; gap: 12px; } #ctc-welcome-notice .ctc-welcome-notice-icon { flex-shrink: 0; width: 40px; height: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-left: 12px; } #ctc-welcome-notice .ctc-welcome-notice-icon .dashicons { color: #fff; font-size: 20px; width: 20px; height: 20px; } #ctc-welcome-notice .ctc-welcome-notice-text { flex: 1; } #ctc-welcome-notice .ctc-welcome-notice-title { margin: 0 0 4px 0; font-size: 14px; } #ctc-welcome-notice .ctc-welcome-emoji { margin-left: 4px; } #ctc-welcome-notice .ctc-welcome-notice-message { margin: 0 0 12px 0; color: #50575e; } #ctc-welcome-notice .ctc-welcome-telemetry-opt-in { margin: 12px 0; } #ctc-welcome-notice .ctc-welcome-telemetry-label { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; font-size: 13px; color: #1d2327; } #ctc-welcome-notice .ctc-welcome-telemetry-label input { margin-top: 2px; flex-shrink: 0; } #ctc-welcome-notice .ctc-welcome-telemetry-note { margin: 4px 0 0 27px; font-size: 12px; color: #646970; line-height: 1.4; } #ctc-welcome-notice .ctc-welcome-telemetry-note a { color: #2271b1; } #ctc-welcome-notice .ctc-welcome-notice-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; } #ctc-welcome-notice .ctc-welcome-notice-actions .button-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-color: #667eea; } #ctc-welcome-notice .ctc-welcome-notice-actions .button-primary:hover { background: linear-gradient(135deg, #5a6fd6 0%, #6a4190 100%); border-color: #5a6fd6; } #ctc-welcome-notice .ctc-welcome-link { color: #2271b1; text-decoration: none; font-weight: 500; margin-left: 8px; } #ctc-welcome-notice .ctc-welcome-link:hover { color: #135e96; } '; wp_add_inline_style( 'wp-admin', $styles ); // Inline script for handling dismissal, Get Started, and checkbox opt-in. $dismiss_nonce = wp_create_nonce( 'ctc_dismiss_welcome_notice' ); $opt_in_nonce = wp_create_nonce( 'ctc_telemetry_opt_in' ); $script = " jQuery(document).ready(function($) { function getOptIn() { return $('#ctc-telemetry-opt-in-checkbox').is(':checked') ? '1' : '0'; } function updateOptIn(callback) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'ctc_update_telemetry_opt_in', nonce: '" . esc_js( $opt_in_nonce ) . "', opt_in: getOptIn() } }).always(function() { if (callback) callback(); }); } function dismissNotice(callback) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'ctc_dismiss_welcome_notice', nonce: '" . esc_js( $dismiss_nonce ) . "', telemetry_opt_in: getOptIn() } }).always(function() { if (callback) callback(); }); } $('#ctc-telemetry-opt-in-checkbox').on('change', function() { updateOptIn(); }); $('#ctc-welcome-notice').on('click', '.notice-dismiss', function() { dismissNotice(); }); $('#ctc-welcome-notice .ctc-welcome-notice-actions .button-primary').on('click', function(e) { var href = $(this).attr('href'); if (href) { e.preventDefault(); dismissNotice(function() { window.location.href = href; }); } }); }); "; wp_add_inline_script( 'jquery', $script ); } /** * AJAX handler for dismissing the welcome notice. * * @since 5.0.0 */ public function ajax_dismiss_notice() { // Verify nonce. if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ctc_dismiss_welcome_notice' ) ) { wp_send_json_error( 'Invalid nonce' ); } // Check permissions. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( 'Insufficient permissions' ); } // Save telemetry opt-in (default opt-out if not explicitly set). // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above. $opt_in = isset( $_POST['telemetry_opt_in'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['telemetry_opt_in'] ) ); Telemetry::set_opt_in( $opt_in ); $telemetry = Telemetry::get(); if ( $opt_in ) { $telemetry->maybe_send(); $telemetry->schedule_cron(); } else { $telemetry->unschedule_cron(); } // Mark as dismissed. update_option( self::NOTICE_DISMISSED_OPTION, true ); wp_send_json_success(); } /** * Handle version updates - reset notice for major upgrades. * * @since 5.0.0 * * @param string $from_version Previous version. * @param string $to_version New version. */ public function on_version_update( $from_version, $to_version ) { // Show welcome notice again for major version upgrades. $from_major = explode( '.', $from_version )[0]; $to_major = explode( '.', $to_version )[0]; if ( $from_major !== $to_major ) { delete_option( self::NOTICE_DISMISSED_OPTION ); } } /** * Reset welcome notice (useful for testing). * * @since 5.0.0 */ public static function reset_notice() { delete_option( self::NOTICE_DISMISSED_OPTION ); } }