Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions src/class-tiny-onboarding.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?php
/*
* Tiny Compress Images - WordPress plugin.
* Copyright (C) 2015-2018 Tinify B.V.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/**
* Class responsible for onboarding a new user
*/
class Tiny_Onboarding extends Tiny_WP_Base {

/**
* Prefix of every onboarding step page slug.
*
* @var string
*/
const PAGE_SLUG = 'tiny-onboarding';

/**
* @var string
*/
private $page_title;

/**
* Tiny settings
*
* @var Tiny_Settings
*/
private $settings;

/**
* @param Tiny_Settings $settings
*/
public function __construct( $settings ) {
parent::__construct();
$this->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 );
}
}
}
1 change: 1 addition & 0 deletions src/class-tiny-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public function init() {
);

$this->tiny_compatibility();
new Tiny_Onboarding( $this->settings );
}

public function cli_init() {
Expand Down
12 changes: 11 additions & 1 deletion src/class-tiny-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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' ) );
}
Expand Down
17 changes: 17 additions & 0 deletions src/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
28 changes: 28 additions & 0 deletions src/js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -295,6 +313,7 @@
});
}

console.log('adminpage:', adminpage);
switch (adminpage) {
case 'upload-php':
eventOn('click', 'button.tiny-compress', compressImage);
Expand All @@ -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]');
Expand Down
20 changes: 20 additions & 0 deletions src/views/onboarding-1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<div class="tiny-onboarding">
<h1><?php esc_html_e( 'Tinify Account', 'tiny-compress-images' ); ?></h1>
<p>
<?php
esc_html_e(
'Increase performance and save space with the best compression algorithm in WordPress.',
'tiny-compress-images'
);
?>
</p>
<div id="tiny-onboarding-step">
<?php $this->settings->render_account_status(); ?>
</div>
<p class="tiny-onboarding-actions" id="tiny-onboarding-continue" style="display: none">
<a class="button button-primary button-hero"
href="<?php echo esc_url( $this->get_step_url( 2 ) ); ?>">
<?php esc_html_e( 'Continue', 'tiny-compress-images' ); ?>
</a>
</p>
</div>
64 changes: 64 additions & 0 deletions src/views/onboarding-2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/*
Onboarding presents both options enabled. A site that has explicitly turned
one of them off keeps that choice. */
$tiny_compression_timing = get_option( self::get_prefixed_name( 'compression_timing' ) );
$tiny_auto_compress = 'manual' !== $tiny_compression_timing;

$tiny_convert_format = get_option( self::get_prefixed_name( 'convert_format' ) );
$tiny_convert = ! isset( $tiny_convert_format['convert'] ) ||
'on' === $tiny_convert_format['convert'];

$tiny_timing_field = self::get_prefixed_name( 'compression_timing' );
$tiny_convert_field = self::get_prefixed_name( 'convert_format' );
?>

<div class="tiny-onboarding">
<h1><?php esc_html_e( 'How should TinyPNG work?', 'tiny-compress-images' ); ?></h1>
<p class="tiny-text-center">
<?php esc_html_e( 'These two options cover most sites.', 'tiny-compress-images' ); ?>
<br>
<?php
/* translators: "Settings → TinyPNG" is the path to the plugin's settings page in the admin menu. */
esc_html_e(
'You can fine-tune the rest any time under Settings → TinyPNG.',
'tiny-compress-images'
);
?>
</p>

<div class="tiny-card">
<p class="tiny-check">
<input type="hidden" name="<?php echo esc_attr( $tiny_timing_field ); ?>" value="manual">
<input type="checkbox" id="<?php echo esc_attr( $tiny_timing_field ); ?>"
name="<?php echo esc_attr( $tiny_timing_field ); ?>" value="background"
<?php checked( $tiny_auto_compress ); ?>>
<label for="<?php echo esc_attr( $tiny_timing_field ); ?>">
<strong><?php esc_html_e( 'Compress new images automatically', 'tiny-compress-images' ); ?></strong>
<span class="description"><?php esc_html_e( 'Every new image will be compressed in the background.', 'tiny-compress-images' ); ?></span>
</label>
</p>
<p class="tiny-check">
<input type="hidden" name="<?php echo esc_attr( $tiny_convert_field ); ?>[convert]" value="off">
<input type="checkbox" id="<?php echo esc_attr( $tiny_convert_field ); ?>"
name="<?php echo esc_attr( $tiny_convert_field ); ?>[convert]" value="on"
<?php checked( $tiny_convert ); ?>>
<label for="<?php echo esc_attr( $tiny_convert_field ); ?>">
<strong><?php esc_html_e( 'Generate optimized image formats', 'tiny-compress-images' ); ?></strong>
<span class="description"><?php esc_html_e( 'Also serve WebP or AVIF where the browser supports it.', 'tiny-compress-images' ); ?></span>
</label>
</p>
</div>

<p class="tiny-onboarding-actions">
<a class="button button-primary button-hero"
href="<?php echo esc_url( admin_url( 'upload.php?page=tiny-bulk-optimization' ) ); ?>">
<?php esc_html_e( 'Optimize my images', 'tiny-compress-images' ); ?>
</a>
</p>
<p class="tiny-onboarding-secondary description">
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=tinify' ) ); ?>">
<?php esc_html_e( 'Review all settings', 'tiny-compress-images' ); ?>
</a>
</p>
</div>
Loading
Loading