diff --git a/src/class-tiny-onboarding.php b/src/class-tiny-onboarding.php new file mode 100644 index 00000000..97887def --- /dev/null +++ b/src/class-tiny-onboarding.php @@ -0,0 +1,194 @@ +settings = $settings; + } + + function admin_init() { + if ( $this->is_onboarded() ) { + return; + } + + $this->set_is_onboarded( 1 ); + + if ( self::is_bulk_activation() ) { + return; + } + + wp_safe_redirect( $this->get_step_url( 1 ) ); + exit(); + } + + /** + * Onboarding is skipped when activating multiple plugins + * + * @return bool + */ + private static function is_bulk_activation() { + return filter_has_var( INPUT_GET, 'activate-multi' ); + } + + function admin_menu() { + $this->page_title = __( 'Welcome to TinyPNG', 'tiny-compress-images' ); + + foreach ( $this->steps as $step ) { + $slug = $this->get_step_slug( $step ); + + $hook = add_submenu_page( + 'options-general.php', + $this->page_title, + $this->page_title, + 'manage_options', + $slug, + function () use ( $step ) { + include __DIR__ . '/views/onboarding-' . $step . '.php'; + } + ); + + if ( ! $hook ) { + continue; + } + + remove_submenu_page( 'options-general.php', $slug ); + + /** + * because title is retrieved from submenu, which is not part of the menu, + * resolve it through globals + */ + add_action( 'load-' . $hook, $this->get_method( 'set_page_title' ) ); + } + } + + /** + * Supplies the title of a page that is not listed in the menu. + * + * Hooked to `load-{$hook}` of every onboarding step. + */ + public function set_page_title() { + $GLOBALS['title'] = $this->page_title; + } + + /** + * Checks wether user is onboarded + * defaults to true + * + * @return boolean true if onboarded + */ + function is_onboarded() { + $onboarding_status_field = self::get_prefixed_name( 'onboarding_status' ); + return 1 === (int) get_option( $onboarding_status_field, 1 ); + } + + /** + * Returns the page slug of the given onboarding step + * + * @param int $step + * @return string + */ + private function get_step_slug( $step ) { + return self::PAGE_SLUG . '-' . $step; + } + + /** + * Whether the current admin request is one of the onboarding steps. + * + * Reads the page WordPress itself resolved, so no request input is touched. + * + * @return bool + */ + public static function is_onboarding_page() { + $page = isset( $GLOBALS['plugin_page'] ) ? $GLOBALS['plugin_page'] : ''; + + return 0 === strpos( $page, self::PAGE_SLUG . '-' ); + } + + /** + * Retrieves the url of the given onboarding step + * + * @param int $step + * @return string + */ + public function get_step_url( $step ) { + return admin_url( 'options-general.php?page=' . $this->get_step_slug( $step ) ); + } + + /** + * Sets the onboarding status + * + * @param int $is_onboarded status + */ + static function set_is_onboarded( $is_onboarded ) { + $onboarding_status_field = self::get_prefixed_name( 'onboarding_status' ); + return update_option( $onboarding_status_field, $is_onboarded ); + } + + function render_register() { + $compressor = $this->settings->get_compressor(); + if ( $compressor->can_create_key() ) { + include __DIR__ . '/views/account-status-create-advanced.php'; + } else { + include __DIR__ . '/views/account-status-create-simple.php'; + } + } + + /** + * Decides on activation whether this site still needs onboarding. + * + * A site that already has a key, or that has compressed before, keeps the + * default onboarded state so it is never sent through onboarding again. + */ + static function on_activate() { + $api_key = get_option( self::get_prefixed_name( 'api_key' ) ); + $compression_count = get_option( self::get_prefixed_name( 'status' ) ); + + if ( empty( $api_key ) && empty( $compression_count ) ) { + self::set_is_onboarded( 0 ); + } + } +} diff --git a/src/class-tiny-plugin.php b/src/class-tiny-plugin.php index ff0708e0..9761272b 100644 --- a/src/class-tiny-plugin.php +++ b/src/class-tiny-plugin.php @@ -82,6 +82,7 @@ public function init() { ); $this->tiny_compatibility(); + new Tiny_Onboarding( $this->settings ); } public function cli_init() { diff --git a/src/class-tiny-settings.php b/src/class-tiny-settings.php index 342f9d8e..39ebe67f 100644 --- a/src/class-tiny-settings.php +++ b/src/class-tiny-settings.php @@ -108,7 +108,7 @@ public function admin_init() { ); } - if ( current_user_can( 'manage_options' ) ) { + if ( current_user_can( 'manage_options' ) && ! Tiny_Onboarding::is_onboarding_page() ) { $this->setup_incomplete_checks(); } @@ -227,6 +227,16 @@ protected function get_api_key_pending() { } } + public function has_api_key() { + $api_key = $this->get_api_key(); + if ( empty( $api_key ) ) { + return false; + } + + $pending_key = $this->get_api_key_pending(); + return ! empty( $pending_key ); + } + protected function clear_api_key_pending() { delete_option( self::get_prefixed_name( 'api_key_pending' ) ); } diff --git a/src/css/admin.css b/src/css/admin.css index 977dadf1..acd2a259 100644 --- a/src/css/admin.css +++ b/src/css/admin.css @@ -481,4 +481,21 @@ fieldset.tinypng_convert_fields[disabled] { .tiny-mt-2 { margin-top: 10px; +} + +.tiny-onboarding { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.tiny-card { + background: #fff; + border: 1px solid #E1E1E1; + border-radius: 8px; + padding: 12px; +} +.tiny-text-center { + text-align: center; } \ No newline at end of file diff --git a/src/js/admin.js b/src/js/admin.js index 03312c0a..8c2f7033 100644 --- a/src/js/admin.js +++ b/src/js/admin.js @@ -135,6 +135,22 @@ return false; } + /** + * Does nothing outside of onboarding + * If key is active, shows continue button + */ + function updateOnboardingContinue() { + const button = jQuery('#tiny-onboarding-continue'); + if (!button.length) { + return; + } + + const status = jQuery('#tiny-account-status p.status').closest('div.status'); + const valid = status.hasClass('status-success') || status.hasClass('status-pending'); + + button.toggle(valid); + } + function submitKey(event) { event.preventDefault(); jQuery(event.target).attr({disabled: true}).addClass('loading'); @@ -175,6 +191,8 @@ jQuery.get(ajaxurl + (ajaxurl.indexOf( '?' ) > 0 ? '&' : '?') + 'action=tiny_account_status', function(data) { jQuery(event.target).attr({disabled: false}).removeClass('loading'); target.replaceWith(data); + // The refreshed markup reports whether the key actually works. + updateOnboardingContinue(); }); } jQuery('div.tiny-notice[data-name="setting"]').remove(); @@ -295,6 +313,7 @@ }); } + console.log('adminpage:', adminpage); switch (adminpage) { case 'upload-php': eventOn('click', 'button.tiny-compress', compressImage); @@ -312,6 +331,15 @@ case 'post-php': eventOn('click', 'button.tiny-compress', compressImage); break; + case 'settings_page_tiny-onboarding-1': + changeEnterKeyTarget('div.tiny-account-status create', '[data-tiny-action=create-key]'); + changeEnterKeyTarget('div.tiny-account-status update', '[data-tiny-action=update-key]'); + + eventOn('click', '[data-tiny-action=create-key]', submitKey); + eventOn('click', '[data-tiny-action=update-key]', submitKey); + + updateOnboardingContinue(); + break; case 'settings_page_tinify': changeEnterKeyTarget('div.tiny-account-status create', '[data-tiny-action=create-key]'); changeEnterKeyTarget('div.tiny-account-status update', '[data-tiny-action=update-key]'); diff --git a/src/views/onboarding-1.php b/src/views/onboarding-1.php new file mode 100644 index 00000000..c6b8fa3c --- /dev/null +++ b/src/views/onboarding-1.php @@ -0,0 +1,20 @@ +
+

+

+ +

+
+ settings->render_account_status(); ?> +
+ +
diff --git a/src/views/onboarding-2.php b/src/views/onboarding-2.php new file mode 100644 index 00000000..c933515f --- /dev/null +++ b/src/views/onboarding-2.php @@ -0,0 +1,64 @@ + + +
+

+

+ +
+ +

+ +
+

+ + > + +

+

+ + > + +

+
+ +

+ + + +

+

+ + + +

+
diff --git a/tiny-compress-images.php b/tiny-compress-images.php index 37ccc7e0..ca34d7ce 100644 --- a/tiny-compress-images.php +++ b/tiny-compress-images.php @@ -28,6 +28,7 @@ require dirname( __FILE__ ) . '/src/class-tiny-conversion.php'; require dirname( __FILE__ ) . '/src/class-tiny-picture.php'; require dirname( __FILE__ ) . '/src/class-tiny-apache-rewrite.php'; +require dirname( __FILE__ ) . '/src/class-tiny-onboarding.php'; require dirname( __FILE__ ) . '/src/compatibility/wpml/class-tiny-wpml.php'; require dirname( __FILE__ ) . '/src/compatibility/as3cf/class-tiny-as3cf.php'; require dirname( __FILE__ ) . '/src/compatibility/woocommerce/class-tiny-woocommerce.php'; @@ -46,3 +47,5 @@ __FILE__, array( 'Tiny_Plugin', 'uninstall' ) ); + +register_activation_hook(__FILE__, array('Tiny_Onboarding', 'on_activate') ); \ No newline at end of file