diff --git a/inc/css/parrot.css b/inc/css/parrot.css index 44bdce3..6ff1fbb 100644 --- a/inc/css/parrot.css +++ b/inc/css/parrot.css @@ -1,3 +1,198 @@ +/* Support Parrot admin screen */ +.ti-parrot-wrap { + max-width: 900px; +} + +.ti-parrot-header { + display: flex; + align-items: center; + gap: 12px; + margin: 10px 0 20px; +} + +.ti-parrot-header h1 { + margin: 0; + padding: 0; + font-size: 23px; + font-weight: 600; + line-height: 1.3; +} + +.ti-parrot-logo { + width: auto; + height: 32px; + display: block; +} + +.ti-parrot-header-sep { + width: 1px; + height: 26px; + background: #c3c4c7; +} + +.ti-parrot-notice { + max-width: 720px; + margin: 0 0 16px; +} + +.ti-parrot-status { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + margin-bottom: 16px; + border: 1px solid; + border-radius: 6px; + font-size: 14px; +} + +.ti-parrot-status .dashicons { + width: 20px; + height: 20px; + font-size: 20px; + flex: 0 0 auto; +} + +.ti-parrot-status--active { + background: #edfaef; + border-color: #b8e6c1; + color: #135e96; +} + +.ti-parrot-status--active .dashicons { + color: #00a32a; +} + +.ti-parrot-status--inactive { + background: #f6f7f7; + border-color: #dcdcde; + color: #50575e; +} + +.ti-parrot-status--inactive .dashicons { + color: #8c8f94; +} + +.ti-parrot-card { + background: #fff; + border: 1px solid #c3c4c7; + border-radius: 6px; + padding: 16px 20px; + margin-bottom: 16px; + box-shadow: 0 1px 1px rgba( 0, 0, 0, 0.04 ); +} + +.ti-parrot-intro p { + margin: 0 0 6px; + font-size: 14px; + line-height: 1.6; + color: #3c434a; +} + +.ti-parrot-intro p:last-child { + margin-bottom: 0; +} + +.ti-parrot-intro-hint { + color: #646970; + font-size: 13px; + font-weight: 600; +} + +.ti-parrot-details-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 4px; +} + +.ti-parrot-details-title { + font-size: 14px; + font-weight: 600; + color: #1d2327; +} + +.ti-parrot-row { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 0; + border-top: 1px solid #f0f0f1; +} + +.ti-parrot-row-label { + flex: 0 0 150px; + font-size: 13px; + color: #646970; +} + +.ti-parrot-row-value { + flex: 1; + text-align: right; + font-size: 13px; + color: #1d2327; + word-break: break-all; +} + +.ti-parrot-mono { + font-family: Consolas, Monaco, monospace; +} + +.ti-parrot-copy .dashicons { + width: 18px; + height: 18px; + font-size: 18px; + vertical-align: text-bottom; +} + +.ti-parrot-copy-label { + margin-left: 4px; +} + +.ti-parrot-copy.ti-parrot-copied { + border-color: #00a32a; + color: #00a32a; +} + +.ti-parrot-copy.button-primary.ti-parrot-copied { + background: #00a32a; + border-color: #00a32a; + color: #fff; +} + +.ti-parrot-copy.button-primary.ti-parrot-copied:hover, +.ti-parrot-copy.button-primary.ti-parrot-copied:focus { + background: #00a32a; + border-color: #00a32a; + color: #fff; +} + +.ti-parrot-expiry { + margin: 12px 0 0; + font-size: 12px; + color: #646970; +} + +.ti-parrot-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.ti-parrot-actions .ti-parrot-release { + color: #b32d2e; + border-color: #b32d2e; + background: #fff; +} + +.ti-parrot-actions .ti-parrot-release:hover, +.ti-parrot-actions .ti-parrot-release:focus { + color: #fff; + background: #b32d2e; + border-color: #b32d2e; +} + #pp-log-console .pp-log { overflow: hidden; } diff --git a/inc/js/parrot.js b/inc/js/parrot.js index 61c9636..e464aa9 100644 --- a/inc/js/parrot.js +++ b/inc/js/parrot.js @@ -7,6 +7,19 @@ ); function init() { + $( document ).on( + "click", ".ti-parrot-copy", function( e ){ + e.preventDefault(); + var button = this; + var text = button.getAttribute( "data-clipboard-text" ) || ""; + copyText( text ).then( + function(){ + showCopied( button ); + } + ).catch( function(){} ); + } + ); + $( '#pp-flush' ).on( "click", function(e){ e.preventDefault(); @@ -83,6 +96,62 @@ ); } + function copyText( text ) { + if ( navigator.clipboard && navigator.clipboard.writeText ) { + return navigator.clipboard.writeText( text ).catch( function(){ + return legacyCopy( text ); + } ); + } + return legacyCopy( text ); + } + + function legacyCopy( text ) { + return new Promise( + function( resolve, reject ){ + var area = document.createElement( "textarea" ); + var ok = false; + area.value = text; + area.style.position = "fixed"; + area.style.opacity = "0"; + document.body.appendChild( area ); + area.focus(); + area.select(); + try { + ok = document.execCommand( "copy" ); + } catch ( err ) {} + document.body.removeChild( area ); + if ( ok ) { + resolve(); + } else { + reject( new Error( "copy_failed" ) ); + } + } + ); + } + + function showCopied( button ) { + var $button = $( button ); + var $icon = $button.find( ".dashicons" ); + var $label = $button.find( ".ti-parrot-copy-label" ); + var prev = $label.length ? $label.text() : ""; + + $button.addClass( "ti-parrot-copied" ); + $icon.removeClass( "dashicons-clipboard" ).addClass( "dashicons-yes" ); + if ( $label.length ) { + $label.text( ( typeof pp !== "undefined" && pp.copied ) ? pp.copied : "Copied!" ); + } + + setTimeout( + function(){ + $button.removeClass( "ti-parrot-copied" ); + $icon.removeClass( "dashicons-yes" ).addClass( "dashicons-clipboard" ); + if ( $label.length ) { + $label.text( prev ); + } + }, 1600 + ); + } + function showSpinner() { $( '#pp-spinner' ).css( 'visibility', 'visible' ).attr( 'aria-hidden', 'false' ).show(); } diff --git a/pirate-parrot.php b/pirate-parrot.php index eb5f725..9c67537 100644 --- a/pirate-parrot.php +++ b/pirate-parrot.php @@ -1,9 +1,9 @@ get_options(); add_action( 'admin_menu', array( $this, 'register_settings_page' ) ); + register_activation_hook( __FILE__, array( $this, 'wake_bird' ) ); register_deactivation_hook( __FILE__, array( $this, 'sleep_bird' ) ); add_action( 'ti_kill_parrot', array( $this, 'sleep_bird' ) ); add_action( 'init', array( $this, 'init' ) ); + add_action( 'admin_init', array( $this, 'maybe_activation_redirect' ) ); + } + + function wake_bird() { + set_transient( 'ti_parrot_activation_redirect', true, 5 * MINUTE_IN_SECONDS ); + } + + function maybe_activation_redirect() { + if ( ! get_transient( 'ti_parrot_activation_redirect' ) ) { + return; + } + delete_transient( 'ti_parrot_activation_redirect' ); + + // Avoid breaking AJAX/REST/cron requests. + if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) { + return; + } + + // Only redirect users who can actually access the screen. + if ( is_network_admin() || ! current_user_can( 'manage_options' ) ) { + return; + } + + // Don't redirect when activating multiple plugins at once. + if ( isset( $_GET['activate-multi'] ) ) { + return; + } + + wp_safe_redirect( admin_url( 'tools.php?page=ti_pirate_parrot' ) ); + exit; } function init() { @@ -94,7 +125,8 @@ function admin_enqueue_scripts() { 'pirate-parrot', 'pp', array( - 'nonce' => wp_create_nonce( 'parrot' ), + 'nonce' => wp_create_nonce( 'parrot' ), + 'copied' => __( 'Copied!', 'pirate-parrot' ), ) ); @@ -235,9 +267,7 @@ function register_settings_page() { ) ); - if ( $this->is_user_parrot() ) { - add_action( 'load-' . $submenu, array( $this, 'load_js_and_css' ) ); - } + add_action( 'load-' . $submenu, array( $this, 'load_js_and_css' ) ); } function is_user_parrot() { @@ -283,53 +313,54 @@ function ti_parrot_cage() { // delete the account if it's expired $this->kill_sleep_bird(); } - printf( - ' - - - -
%3$s
- - %6$s - - -%1$s
', - sprintf( - 'Parrot info:%1$s
', - ( ! is_wp_error( $expiration_date = $this->get_expiration_date() ) - ? 'This parrot will leave on ' . esc_html( $expiration_date ) - : $expiration_date->get_error_message() - ) - ); - } + function get_parrot_info_rows() { + $theme = wp_get_theme(); - return $output; + return array( + array( + 'label' => __( 'Access token', 'pirate-parrot' ), + 'value' => isset( $this->_options['token'] ) ? $this->_options['token'] : '', + 'mono' => true, + ), + array( + 'label' => __( 'Login URL', 'pirate-parrot' ), + 'value' => wp_login_url(), + ), + array( + 'label' => __( 'WordPress version', 'pirate-parrot' ), + 'value' => get_bloginfo( 'version' ), + ), + array( + 'label' => __( 'PHP version', 'pirate-parrot' ), + 'value' => phpversion(), + ), + array( + 'label' => __( 'Site locale', 'pirate-parrot' ), + 'value' => get_locale(), + ), + array( + 'label' => __( 'Theme', 'pirate-parrot' ), + 'value' => trim( $theme->get( 'Name' ) . ' ' . $theme->get( 'Version' ) ), + ), + ); + } + + function render_parrot_details() { + $rows = $this->get_parrot_info_rows(); + + // Build the "copy all" payload, one "Label: value" per line. + $lines = array(); + foreach ( $rows as $row ) { + $lines[] = $row['label'] . ': ' . $row['value']; + } + $copy_all = implode( "\n", $lines ); + $expiration = $this->get_expiration_date(); + ?> ++ get_error_message() ); + } + ?> +
+%1$s
', esc_html( $message ) ); } } else { - $output = sprintf( '%1$s
', $message->get_error_message() ); + $output = sprintf( '%1$s
', esc_html( $message->get_error_message() ) ); } if ( '' !== $output ) { $output = sprintf( - '