updated plugin Jetpack Protect version 1.0.4

This commit is contained in:
2022-09-02 15:19:40 +00:00
committed by Gitium
parent ba0955a33f
commit 1ecbe8cf47
99 changed files with 8263 additions and 935 deletions

View File

@ -0,0 +1,312 @@
<?php
/**
* Jetpack's JITM class.
*
* @package automattic/jetpack-jitm
*/
namespace Automattic\Jetpack\JITMS;
use Automattic\Jetpack\Assets;
use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Status;
/**
* Jetpack just in time messaging through out the admin
*
* @since 1.1.0
* @since-jetpack 5.6.0
*/
class JITM {
const PACKAGE_VERSION = '2.2.26';
/**
* The configuration method that is called from the jetpack-config package.
*/
public static function configure() {
$jitm = self::get_instance();
$jitm->register();
}
/**
* Pre/Post Connection JITM factory metod
*
* @return Post_Connection_JITM|Pre_Connection_JITM JITM instance.
*/
public static function get_instance() {
if ( ( new Connection_Manager() )->is_connected() ) {
$jitm = new Post_Connection_JITM();
} else {
$jitm = new Pre_Connection_JITM();
}
return $jitm;
}
/**
* Sets up JITM action callbacks if needed.
*/
public function register() {
if ( did_action( 'jetpack_registered_jitms' ) ) {
// JITMs have already been registered.
return;
}
if ( ! $this->jitms_enabled() ) {
// Do nothing.
return;
}
add_action( 'rest_api_init', array( __NAMESPACE__ . '\\Rest_Api_Endpoints', 'register_endpoints' ) );
add_action( 'current_screen', array( $this, 'prepare_jitms' ) );
/**
* These are sync actions that we need to keep track of for jitms.
*/
add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
/**
* Fires when the JITMs are registered. This action is used to ensure that
* JITMs are registered only once.
*
* @since 1.16.0
*/
do_action( 'jetpack_registered_jitms' );
}
/**
* Checks the jetpack_just_in_time_msgs filters and whether the site
* is offline to determine whether JITMs are enabled.
*
* @return bool True if JITMs are enabled, else false.
*/
public function jitms_enabled() {
/**
* Filter to turn off all just in time messages
*
* @since 1.1.0
* @since-jetpack 3.7.0
* @since-jetpack 5.4.0 Correct docblock to reflect default arg value
*
* @param bool true Whether to show just in time messages.
*/
if ( ! apply_filters( 'jetpack_just_in_time_msgs', true ) ) {
return false;
}
// Folks cannot connect to WordPress.com and won't really be able to act on the pre-connection messages. So bail.
if ( ( new Status() )->is_offline_mode() ) {
return false;
}
return true;
}
/**
* Prepare actions according to screen and post type.
*
* @since 1.1.0
* @since-jetpack 3.8.2
*
* @uses Jetpack_Autoupdate::get_possible_failures()
*
* @param \WP_Screen $screen WP Core's screen object.
*/
public function prepare_jitms( $screen ) {
/**
* Filter to hide JITMs on certain screens.
*
* @since 1.14.0
*
* @param bool true Whether to show just in time messages.
* @param string $string->id The ID of the current screen.
*/
if ( apply_filters( 'jetpack_display_jitms_on_screen', true, $screen->id ) ) {
add_action( 'admin_enqueue_scripts', array( $this, 'jitm_enqueue_files' ) );
add_action( 'admin_notices', array( $this, 'ajax_message' ) );
add_action( 'edit_form_top', array( $this, 'ajax_message' ) );
}
}
/**
* Function to enqueue jitm css and js
*/
public function jitm_enqueue_files() {
if ( $this->is_gutenberg_page() ) {
return;
}
Assets::register_script(
'jetpack-jitm',
'../build/index.js',
__FILE__,
array(
'in_footer' => true,
'dependencies' => array( 'jquery' ),
)
);
Assets::enqueue_script( 'jetpack-jitm' );
wp_localize_script(
'jetpack-jitm',
'jitm_config',
array(
'api_root' => esc_url_raw( rest_url() ),
'activate_module_text' => esc_html__( 'Activate', 'jetpack-jitm' ),
'activated_module_text' => esc_html__( 'Activated', 'jetpack-jitm' ),
'activating_module_text' => esc_html__( 'Activating', 'jetpack-jitm' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
)
);
}
/**
* Is the current page a block editor page?
*
* @since 1.1.0
* @since-jetpack 8.0.0
*/
public function is_gutenberg_page() {
$current_screen = get_current_screen();
return ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() );
}
/**
* Get's the current message path for display of a JITM
*
* @return string The message path
*/
public function get_message_path() {
$screen = get_current_screen();
return 'wp:' . $screen->id . ':' . current_filter();
}
/**
* Injects the dom to show a JITM inside of wp-admin.
*/
public function ajax_message() {
if ( ! is_admin() ) {
return;
}
// do not display on Gutenberg pages.
if ( $this->is_gutenberg_page() ) {
return;
}
$message_path = $this->get_message_path();
$query_string = _http_build_query( $_GET, '', ',' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_screen = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- Escaped below
?>
<div class="jetpack-jitm-message"
data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp_rest' ) ); ?>"
data-ajax-nonce="<?php echo esc_attr( wp_create_nonce( 'wp_ajax_action' ) ); ?>"
data-message-path="<?php echo esc_attr( $message_path ); ?>"
data-query="<?php echo urlencode_deep( $query_string ); ?>"
data-redirect="<?php echo urlencode_deep( $current_screen ); ?>"
></div>
<?php
}
/**
* Generate the icon to display on the JITM.
*
* All icons supported in this method should be included in the array returned by
* JITM::get_supported_icons.
*
* @param string $content_icon Icon type name.
* @param bool $full_jp_logo_exists Is there a big JP logo already displayed on this screen.
*/
public function generate_icon( $content_icon, $full_jp_logo_exists ) {
switch ( $content_icon ) {
case 'jetpack':
$jetpack_logo = new Jetpack_Logo();
$content_icon = '<div class="jp-emblem">' . ( ( $full_jp_logo_exists ) ? $jetpack_logo->get_jp_emblem() : $jetpack_logo->get_jp_emblem_larger() ) . '</div>';
break;
case 'woocommerce':
$content_icon = '<div class="jp-emblem"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 168 100" xml:space="preserve" enable-background="new 0 0 168 100" width="50" height="30"><style type="text/css">
.st0{clip-path:url(#SVGID_2_);enable-background:new ;}
.st1{clip-path:url(#SVGID_4_);}
.st2{clip-path:url(#SVGID_6_);}
.st3{clip-path:url(#SVGID_8_);fill:#8F567F;}
.st4{clip-path:url(#SVGID_10_);fill:#FFFFFE;}
.st5{clip-path:url(#SVGID_12_);fill:#FFFFFE;}
.st6{clip-path:url(#SVGID_14_);fill:#FFFFFE;}
</style><g><defs><polygon id="SVGID_1_" points="83.8 100 0 100 0 0.3 83.8 0.3 167.6 0.3 167.6 100 "/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath><g class="st0"><g><defs><rect id="SVGID_3_" width="168" height="100"/></defs><clipPath id="SVGID_4_"><use xlink:href="#SVGID_3_" overflow="visible"/></clipPath><g class="st1"><defs><path id="SVGID_5_" d="M15.6 0.3H152c8.6 0 15.6 7 15.6 15.6v52c0 8.6-7 15.6-15.6 15.6h-48.9l6.7 16.4L80.2 83.6H15.6C7 83.6 0 76.6 0 67.9v-52C0 7.3 7 0.3 15.6 0.3"/></defs><clipPath id="SVGID_6_"><use xlink:href="#SVGID_5_" overflow="visible"/></clipPath><g class="st2"><defs><rect id="SVGID_7_" width="168" height="100"/></defs><clipPath id="SVGID_8_"><use xlink:href="#SVGID_7_" overflow="visible"/></clipPath><rect x="-10" y="-9.7" class="st3" width="187.6" height="119.7"/></g></g></g></g></g><g><defs><path id="SVGID_9_" d="M8.4 14.5c1-1.3 2.4-2 4.3-2.1 3.5-0.2 5.5 1.4 6 4.9 2.1 14.3 4.4 26.4 6.9 36.4l15-28.6c1.4-2.6 3.1-3.9 5.2-4.1 3-0.2 4.9 1.7 5.6 5.7 1.7 9.1 3.9 16.9 6.5 23.4 1.8-17.4 4.8-30 9-37.7 1-1.9 2.5-2.9 4.5-3 1.6-0.1 3 0.3 4.3 1.4 1.3 1 2 2.3 2.1 3.9 0.1 1.2-0.1 2.3-0.7 3.3 -2.7 5-4.9 13.2-6.6 24.7 -1.7 11.1-2.3 19.8-1.9 26.1 0.1 1.7-0.1 3.2-0.8 4.5 -0.8 1.5-2 2.4-3.7 2.5 -1.8 0.1-3.6-0.7-5.4-2.5C52.4 66.7 47.4 57 43.7 44.1c-4.4 8.8-7.7 15.3-9.9 19.7 -4 7.7-7.5 11.7-10.3 11.9 -1.9 0.1-3.5-1.4-4.8-4.7 -3.5-9-7.3-26.3-11.3-52C7.1 17.3 7.5 15.8 8.4 14.5"/></defs><clipPath id="SVGID_10_"><use xlink:href="#SVGID_9_" overflow="visible"/></clipPath><rect x="-2.7" y="-0.6" class="st4" width="90.6" height="86.4"/></g><g><defs><path id="SVGID_11_" d="M155.6 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.6 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.6-5.9 5.3-12.4 5.3-19.4C159 33.4 157.9 28.9 155.6 25.2zM147 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C147.5 40.9 147.3 42.6 147 44.2z"/></defs><clipPath id="SVGID_12_"><use xlink:href="#SVGID_11_" overflow="visible"/></clipPath><rect x="109.6" y="6.9" class="st5" width="59.4" height="71.4"/></g><g><defs><path id="SVGID_13_" d="M112.7 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.5 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.5-5.9 5.3-12.4 5.3-19.4C116 33.4 114.9 28.9 112.7 25.2zM104.1 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C104.6 40.9 104.4 42.6 104.1 44.2z"/></defs><clipPath id="SVGID_14_"><use xlink:href="#SVGID_13_" overflow="visible"/></clipPath><rect x="66.7" y="6.9" class="st6" width="59.4" height="71.4"/></g></svg></div>';
break;
default:
$content_icon = '';
break;
}
return $content_icon;
}
/**
* Returns an array containing the supported icons for JITMs.
*
* The list includes an empty string, which is used when no icon should be displayed.
*
* @return array The list of supported icons.
*/
public function get_supported_icons() {
return array(
'jetpack',
'woocommerce',
'',
);
}
/**
* Stores dismiss data into an option
*
* @param string $key Dismiss key.
*/
public function save_dismiss( $key ) {
$hide_jitm = \Jetpack_Options::get_option( 'hide_jitm' );
if ( ! is_array( $hide_jitm ) ) {
$hide_jitm = array();
}
if ( ! isset( $hide_jitm[ $key ] ) || ! is_array( $hide_jitm[ $key ] ) ) {
$hide_jitm[ $key ] = array(
'last_dismissal' => 0,
'number' => 0,
);
}
$hide_jitm[ $key ] = array(
'last_dismissal' => time(),
'number' => $hide_jitm[ $key ]['number'] + 1,
);
\Jetpack_Options::update_option( 'hide_jitm', $hide_jitm );
}
/**
* Sets the 'jetpack_last_plugin_sync' transient when the active_plugins option is synced.
*
* @param array $params The action parameters.
*
* @return array Returns the action parameters unchanged.
*/
public function jetpack_track_last_sync_callback( $params ) {
/**
* This filter is documented in the Automattic\Jetpack\JITMS\Post_Connection_JITM class.
*/
if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', true ) ) {
return $params;
}
if ( is_array( $params ) && isset( $params[0] ) ) {
$option = $params[0];
if ( 'active_plugins' === $option ) {
// Use the cache if we can, but not terribly important if it gets evicted.
set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
}
}
return $params;
}
}

View File

@ -0,0 +1,607 @@
<?php
/**
* Jetpack's Post-Connection JITM class.
*
* @package automattic/jetpack-jitm
*/
namespace Automattic\Jetpack\JITMS;
use Automattic\Jetpack\A8c_Mc_Stats;
use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager;
use Automattic\Jetpack\Device_Detection;
use Automattic\Jetpack\Partner;
use Automattic\Jetpack\Redirect;
use Automattic\Jetpack\Tracking;
/**
* Jetpack just in time messaging through out the admin
*
* @since 1.1.0
*
* @since-jetpack 5.6.0
*/
class Post_Connection_JITM extends JITM {
/**
* Tracking object.
*
* @var Automattic\Jetpack\Tracking
*
* @access private
*/
public $tracking;
/**
* JITM constructor.
*/
public function __construct() {
$this->tracking = new Tracking();
}
/**
* Prepare actions according to screen and post type.
*
* @since 1.1.0
* @since-jetpack 3.8.2
*
* @uses Jetpack_Autoupdate::get_possible_failures()
*
* @param \WP_Screen $screen WP Core's screen object.
*/
public function prepare_jitms( $screen ) {
parent::prepare_jitms( $screen );
if ( ! in_array(
$screen->id,
array(
'jetpack_page_akismet-key-config',
'admin_page_jetpack_modules',
),
true
) ) {
// Not really a JITM. Don't know where else to put this :) .
add_action( 'admin_notices', array( $this, 'delete_user_update_connection_owner_notice' ) );
}
}
/**
* A special filter for WooCommerce, to set a message based on local state.
*
* @param string $content The current message.
*
* @return array The new message.
*/
public static function jitm_woocommerce_services_msg( $content ) {
if ( ! function_exists( 'wc_get_base_location' ) ) {
return $content;
}
$base_location = wc_get_base_location();
switch ( $base_location['country'] ) {
case 'US':
$content->message = esc_html__( 'New free service: Show USPS shipping rates on your store! Added bonus: print shipping labels without leaving WooCommerce.', 'jetpack-jitm' );
break;
case 'CA':
$content->message = esc_html__( 'New free service: Show Canada Post shipping rates on your store!', 'jetpack-jitm' );
break;
default:
$content->message = '';
}
return $content;
}
/**
* A special filter for WooCommerce Call To Action button
*
* @return string The new CTA
*/
public static function jitm_jetpack_woo_services_install() {
return wp_nonce_url(
add_query_arg(
array(
'wc-services-action' => 'install',
),
admin_url( 'admin.php?page=wc-settings' )
),
'wc-services-install'
);
}
/**
* A special filter for WooCommerce Call To Action button.
*
* @return string The new CTA
*/
public static function jitm_jetpack_woo_services_activate() {
return wp_nonce_url(
add_query_arg(
array(
'wc-services-action' => 'activate',
),
admin_url( 'admin.php?page=wc-settings' )
),
'wc-services-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to install the Creative Mail plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_creative_mail_install() {
return wp_nonce_url(
add_query_arg(
array(
'creative-mail-action' => 'install',
),
admin_url( 'edit.php?post_type=feedback' )
),
'creative-mail-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to activate the Creative Mail plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_creative_mail_activate() {
return wp_nonce_url(
add_query_arg(
array(
'creative-mail-action' => 'activate',
),
admin_url( 'edit.php?post_type=feedback' )
),
'creative-mail-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to install the Jetpack Backup plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_backup_install() {
return wp_nonce_url(
add_query_arg(
array(
'jetpack-backup-action' => 'install',
),
admin_url( 'admin.php?page=jetpack' )
),
'jetpack-backup-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to activate the Jetpack Backup plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_backup_activate() {
return wp_nonce_url(
add_query_arg(
array(
'jetpack-backup-action' => 'activate',
),
admin_url( 'admin.php?page=jetpack' )
),
'jetpack-backup-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to install the Jetpack Boost plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_boost_install() {
return wp_nonce_url(
add_query_arg(
array(
'jetpack-boost-action' => 'install',
),
admin_url( 'admin.php?page=jetpack' )
),
'jetpack-boost-install'
);
}
/**
* A special filter used in the CTA of a JITM offering to activate the Jetpack Boost plugin.
*
* @return string The new CTA
*/
public static function jitm_jetpack_boost_activate() {
return wp_nonce_url(
add_query_arg(
array(
'jetpack-boost-action' => 'activate',
),
admin_url( 'admin.php?page=jetpack' )
),
'jetpack-boost-install'
);
}
/**
* This is an entire admin notice dedicated to messaging and handling of the case where a user is trying to delete
* the connection owner.
*/
public function delete_user_update_connection_owner_notice() {
global $current_screen;
/*
* phpcs:disable WordPress.Security.NonceVerification.Recommended
*
* This function is firing within wp-admin and checks (below) if it is in the midst of a deletion on the users
* page. Nonce will be already checked by WordPress, so we do not need to check ourselves.
*/
if ( ! isset( $current_screen->base ) || 'users' !== $current_screen->base ) {
return;
}
if ( ! isset( $_REQUEST['action'] ) || 'delete' !== $_REQUEST['action'] ) {
return;
}
// Get connection owner or bail.
$connection_manager = new Manager();
$connection_owner_id = $connection_manager->get_connection_owner_id();
if ( ! $connection_owner_id ) {
return;
}
$connection_owner_userdata = get_userdata( $connection_owner_id );
// Bail if we're not trying to delete connection owner.
$user_ids_to_delete = array();
if ( isset( $_REQUEST['users'] ) ) {
$user_ids_to_delete = array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['users'] ) );
} elseif ( isset( $_REQUEST['user'] ) ) {
$user_ids_to_delete[] = sanitize_text_field( wp_unslash( $_REQUEST['user'] ) );
}
// phpcs:enable
$user_ids_to_delete = array_map( 'absint', $user_ids_to_delete );
$deleting_connection_owner = in_array( $connection_owner_id, (array) $user_ids_to_delete, true );
if ( ! $deleting_connection_owner ) {
return;
}
// Bail if they're trying to delete themselves to avoid confusion.
if ( get_current_user_id() === $connection_owner_id ) {
return;
}
// Track it!
if ( method_exists( $this->tracking, 'record_user_event' ) ) {
$this->tracking->record_user_event( 'delete_connection_owner_notice_view' );
}
$connected_admins = $connection_manager->get_connected_users( 'jetpack_disconnect' );
$user = is_a( $connection_owner_userdata, 'WP_User' ) ? esc_html( $connection_owner_userdata->data->user_login ) : '';
echo "<div class='notice notice-warning' id='jetpack-notice-switch-connection-owner'>";
echo '<h2>' . esc_html__( 'Important notice about your Jetpack connection:', 'jetpack-jitm' ) . '</h2>';
echo '<p>' . sprintf(
/* translators: WordPress User, if available. */
esc_html__( 'Warning! You are about to delete the Jetpack connection owner (%s) for this site, which may cause some of your Jetpack features to stop working.', 'jetpack-jitm' ),
esc_html( $user )
) . '</p>';
if ( ! empty( $connected_admins ) && count( $connected_admins ) > 1 ) {
echo '<form id="jp-switch-connection-owner" action="" method="post">';
echo "<label for='owner'>" . esc_html__( 'You can choose to transfer connection ownership to one of these already-connected admins:', 'jetpack-jitm' ) . ' </label>';
$connected_admin_ids = array_map(
function ( $connected_admin ) {
return $connected_admin->ID;
},
$connected_admins
);
wp_dropdown_users(
array(
'name' => 'owner',
'include' => array_diff( $connected_admin_ids, array( $connection_owner_id ) ),
'show' => 'display_name_with_login',
)
);
echo '<p>';
submit_button( esc_html__( 'Set new connection owner', 'jetpack-jitm' ), 'primary', 'jp-switch-connection-owner-submit', false );
echo '</p>';
echo "<div id='jp-switch-user-results'></div>";
echo '</form>';
?>
<script type="text/javascript">
jQuery( document ).ready( function( $ ) {
$( '#jp-switch-connection-owner' ).on( 'submit', function( e ) {
var formData = $( this ).serialize();
var submitBtn = document.getElementById( 'jp-switch-connection-owner-submit' );
var results = document.getElementById( 'jp-switch-user-results' );
submitBtn.disabled = true;
$.ajax( {
type : "POST",
url : "<?php echo esc_url( get_rest_url() . 'jetpack/v4/connection/owner' ); ?>",
data : formData,
headers : {
'X-WP-Nonce': "<?php echo esc_js( wp_create_nonce( 'wp_rest' ) ); ?>",
},
success: function() {
results.innerHTML = "<?php esc_html_e( 'Success!', 'jetpack-jitm' ); ?>";
setTimeout( function() {
$( '#jetpack-notice-switch-connection-owner' ).hide( 'slow' );
}, 1000 );
}
} ).done( function() {
submitBtn.disabled = false;
} );
e.preventDefault();
return false;
} );
} );
</script>
<?php
} else {
echo '<p>' . esc_html__( 'Every Jetpack site needs at least one connected admin for the features to work properly. Please connect to your WordPress.com account via the button below. Once you connect, you may refresh this page to see an option to change the connection owner.', 'jetpack-jitm' ) . '</p>';
$connect_url = $connection_manager->get_authorization_url();
$connect_url = add_query_arg( 'from', 'delete_connection_owner_notice', $connect_url );
echo "<a href='" . esc_url( $connect_url ) . "' target='_blank' rel='noopener noreferrer' class='button-primary'>" . esc_html__( 'Connect to WordPress.com', 'jetpack-jitm' ) . '</a>';
}
echo '<p>';
printf(
wp_kses(
/* translators: URL to Jetpack support doc regarding the primary user. */
__( "<a href='%s' target='_blank' rel='noopener noreferrer'>Learn more</a> about the connection owner and what will break if you do not have one.", 'jetpack-jitm' ),
array(
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
)
),
esc_url( Redirect::get_url( 'jetpack-support-primary-user' ) )
);
echo '</p>';
echo '<p>';
printf(
wp_kses(
/* translators: URL to contact Jetpack support. */
__( 'As always, feel free to <a href="%s" target="_blank" rel="noopener noreferrer">contact our support team</a> if you have any questions.', 'jetpack-jitm' ),
array(
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
)
),
esc_url( Redirect::get_url( 'jetpack-contact-support' ) )
);
echo '</p>';
echo '</div>';
}
/**
* Dismisses a JITM feature class so that it will no longer be shown.
*
* @param string $id The id of the JITM that was dismissed.
* @param string $feature_class The feature class of the JITM that was dismissed.
*
* @return bool Always true.
*/
public function dismiss( $id, $feature_class ) {
$this->tracking->record_user_event(
'jitm_dismiss_client',
array(
'jitm_id' => $id,
'feature_class' => $feature_class,
)
);
$this->save_dismiss( $feature_class );
return true;
}
/**
* Asks the wpcom API for the current message to display keyed on query string and message path
*
* @param string $message_path The message path to ask for.
* @param string $query The query string originally from the front end.
* @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
*
* @return array The JITM's to show, or an empty array if there is nothing to show
*/
public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
// WooCommerce Services.
add_filter( 'jitm_woocommerce_services_msg', array( $this, 'jitm_woocommerce_services_msg' ) );
add_filter( 'jitm_jetpack_woo_services_install', array( $this, 'jitm_jetpack_woo_services_install' ) );
add_filter( 'jitm_jetpack_woo_services_activate', array( $this, 'jitm_jetpack_woo_services_activate' ) );
// Creative Mail.
add_filter( 'jitm_jetpack_creative_mail_install', array( $this, 'jitm_jetpack_creative_mail_install' ) );
add_filter( 'jitm_jetpack_creative_mail_activate', array( $this, 'jitm_jetpack_creative_mail_activate' ) );
// Jetpack Backup.
add_filter( 'jitm_jetpack_backup_install', array( $this, 'jitm_jetpack_backup_install' ) );
add_filter( 'jitm_jetpack_backup_activate', array( $this, 'jitm_jetpack_backup_activate' ) );
// Jetpack Boost.
add_filter( 'jitm_jetpack_boost_install', array( $this, 'jitm_jetpack_boost_install' ) );
add_filter( 'jitm_jetpack_boost_activate', array( $this, 'jitm_jetpack_boost_activate' ) );
$user = wp_get_current_user();
// Unauthenticated or invalid requests just bail.
if ( ! $user ) {
return array();
}
$user_roles = implode( ',', $user->roles );
$site_id = \Jetpack_Options::get_option( 'id' );
// Build our jitm request.
$path = add_query_arg(
array(
'external_user_id' => urlencode_deep( $user->ID ),
'user_roles' => urlencode_deep( $user_roles ),
'query_string' => urlencode_deep( $query ),
'mobile_browser' => Device_Detection::is_smartphone() ? 1 : 0,
'_locale' => get_user_locale(),
),
sprintf( '/sites/%d/jitm/%s', $site_id, $message_path )
);
// Attempt to get from cache.
$envelopes = get_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ) );
// If something is in the cache and it was put in the cache after the last sync we care about, use it.
$use_cache = false;
/**
* Filter to turn off jitm caching
*
* @since 1.1.0
* @since-jetpack 5.4.0
*
* @param bool true Whether to cache just in time messages
*/
if ( apply_filters( 'jetpack_just_in_time_msg_cache', true ) ) {
$use_cache = true;
}
if ( $use_cache ) {
$last_sync = (int) get_transient( 'jetpack_last_plugin_sync' );
$from_cache = $envelopes && $last_sync > 0 && $last_sync < $envelopes['last_response_time'];
} else {
$from_cache = false;
}
// Otherwise, ask again.
if ( ! $from_cache ) {
$wpcom_response = Client::wpcom_json_api_request_as_blog(
$path,
'2',
array(
'user_id' => $user->ID,
'user_roles' => implode( ',', $user->roles ),
),
null,
'wpcom'
);
// silently fail...might be helpful to track it?
if ( is_wp_error( $wpcom_response ) ) {
return array();
}
$envelopes = json_decode( $wpcom_response['body'] );
if ( ! is_array( $envelopes ) ) {
return array();
}
$expiration = isset( $envelopes[0] ) ? $envelopes[0]->ttl : 300;
// Do not cache if expiration is 0 or we're not using the cache.
if ( 0 !== $expiration && $use_cache ) {
$envelopes['last_response_time'] = time();
set_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ), $envelopes, $expiration );
}
}
$hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
unset( $envelopes['last_response_time'] );
/**
* Allow adding your own custom JITMs after a set of JITMs has been received.
*
* @since 1.1.0
* @since-jetpack 6.9.0
* @since-jetpack 8.3.0 - Added Message path.
*
* @param array $envelopes array of existing JITMs.
* @param string $message_path The message path to ask for.
*/
$envelopes = apply_filters( 'jetpack_jitm_received_envelopes', $envelopes, $message_path );
foreach ( $envelopes as $idx => &$envelope ) {
$dismissed_feature = isset( $hidden_jitms[ $envelope->feature_class ] ) && is_array( $hidden_jitms[ $envelope->feature_class ] ) ? $hidden_jitms[ $envelope->feature_class ] : null;
// If the this feature class has been dismissed and the request has not passed the ttl, skip it as it's been dismissed.
if ( is_array( $dismissed_feature ) && ( time() - $dismissed_feature['last_dismissal'] < $envelope->expires || $dismissed_feature['number'] >= $envelope->max_dismissal ) ) {
unset( $envelopes[ $idx ] );
continue;
}
$this->tracking->record_user_event(
'jitm_view_client',
array(
'jitm_id' => $envelope->id,
)
);
$url_params = array(
'u' => $user->ID,
);
// Get affiliate code and add it to the array of URL parameters.
$aff = Partner::init()->get_partner_code( Partner::AFFILIATE_CODE );
if ( '' !== $aff ) {
$url_params['aff'] = $aff;
}
// Check if the current user has connected their WP.com account
// and if not add this information to the the array of URL parameters.
if ( ! ( new Manager() )->is_user_connected( $user->ID ) ) {
$url_params['query'] = 'unlinked=1';
}
$envelope->url = esc_url( Redirect::get_url( "jitm-$envelope->id", $url_params ) );
$stats = new A8c_Mc_Stats();
$envelope->jitm_stats_url = $stats->build_stats_url( array( 'x_jetpack-jitm' => $envelope->id ) );
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
// $CTA is not valid per PHPCS, but it is part of the return from WordPress.com, so allowing.
if ( $envelope->CTA->hook ) {
$envelope->url = apply_filters( 'jitm_' . $envelope->CTA->hook, $envelope->url );
unset( $envelope->CTA->hook );
}
// phpcs:enable
if ( isset( $envelope->content->hook ) ) {
$envelope->content = apply_filters( 'jitm_' . $envelope->content->hook, $envelope->content );
unset( $envelope->content->hook );
}
// No point in showing an empty message.
if ( empty( $envelope->content->message ) ) {
unset( $envelopes[ $idx ] );
continue;
}
$envelope->content->icon = $this->generate_icon( $envelope->content->icon, $full_jp_logo_exists );
$stats->add( 'jitm', $envelope->id . '-viewed' );
$stats->do_server_side_stats();
}
return $envelopes;
}
}

View File

@ -0,0 +1,171 @@
<?php
/**
* Jetpack's Pre-Connection JITM class.
*
* @package automattic/jetpack-jitm
*/
namespace Automattic\Jetpack\JITMS;
/**
* Jetpack pre-connection just in time messaging through out the admin.
*/
class Pre_Connection_JITM extends JITM {
/**
* Filters and formats the messages for the client-side JS renderer
*
* @param string $message_path Current message path.
*
* @return array Formatted messages.
*/
private function filter_messages( $message_path ) {
/**
* Allows filtering of the pre-connection JITMs.
*
* This filter allows plugins to add pre-connection JITMs that will be
* displayed by the JITM package.
*
* @since 1.14.1
*
* @param array An array of pre-connection messages.
*/
$messages = apply_filters( 'jetpack_pre_connection_jitms', array() );
$messages = $this->validate_messages( $messages );
$formatted_messages = array();
foreach ( $messages as $message ) {
if ( ! preg_match( $message['message_path'], $message_path ) ) {
continue;
}
$obj = new \stdClass();
$obj->CTA = array( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
'message' => $message['button_caption'],
'newWindow' => false,
);
$obj->url = $message['button_link'];
$obj->id = $message['id'];
$obj->is_dismissible = true;
$obj->content = array(
'message' => $message['message'],
'description' => $message['description'],
'list' => array(),
'icon' => $this->get_message_icon( $message ),
);
$formatted_messages[] = $obj;
}
return $formatted_messages;
}
/**
* Validates that each of the messages contains all of the required keys:
* - id
* - message_path
* - message
* - description
* - button_link
* - button_caption
*
* @param array $messages An array of JITM messages.
*
* @return array An array of JITM messages that contain all of the required keys.
*/
private function validate_messages( $messages ) {
if ( ! is_array( $messages ) ) {
return array();
}
$expected_keys = array_flip( array( 'id', 'message_path', 'message', 'description', 'button_link', 'button_caption' ) );
foreach ( $messages as $index => $message ) {
if ( count( array_intersect_key( $expected_keys, $message ) ) !== count( $expected_keys ) ) {
// Remove any messages that are missing expected keys.
unset( $messages[ $index ] );
}
}
return $messages;
}
/**
* Get the icon for the message.
*
* The message may contain an 'icon' key. If the value of the 'icon' key matches a supported icon (or empty string), the value is used.
* If the message does not contain an icon key or if the value does not match a supported icon, the Jetpack icon is used by default.
*
* @param array $message A pre-connection JITM.
*
* @return string The icon to use in the JITM.
*/
private function get_message_icon( $message ) {
// Default to the Jetpack icon.
$icon = 'jetpack';
if ( ! isset( $message['icon'] ) ) {
return $icon;
}
$supported_icons = $this->get_supported_icons();
if ( in_array( $message['icon'], $supported_icons, true ) ) {
// Only use the message icon if it's a supported icon or an empty string (for no icon).
$icon = $message['icon'];
}
return $icon;
}
/**
* Retrieve the current message to display keyed on query string and message path
*
* @param string $message_path The message path to ask for.
* @param string $query The query string originally from the front end. Unused in this subclass.
* @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
*
* @return array The JITMs to show, or an empty array if there is nothing to show
*/
public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
if ( ! current_user_can( 'install_plugins' ) ) {
return array();
}
$messages = $this->filter_messages( $message_path );
if ( empty( $messages ) ) {
return array();
}
$hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
foreach ( $messages as $idx => &$envelope ) {
$dismissed_feature = isset( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) &&
is_array( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) ? $hidden_jitms[ 'pre-connection-' . $envelope->id ] : null;
if ( is_array( $dismissed_feature ) ) {
unset( $messages[ $idx ] );
continue;
}
$envelope->content['icon'] = $this->generate_icon( $envelope->content['icon'], $full_jp_logo_exists );
}
return $messages;
}
/**
* Dismisses a JITM ID so that it will no longer be shown.
*
* @param string $id The id of the JITM that was dismissed.
*
* @return bool Always true
*/
public function dismiss( $id ) {
$this->save_dismiss( 'pre-connection-' . $id );
return true;
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* JITM's REST API Endpoints
*
* @package automattic/jetpack-jitm
*/
namespace Automattic\Jetpack\JITMS;
use Automattic\Jetpack\Connection\REST_Connector;
use WP_REST_Server;
/**
* Register the JITM's REST API Endpoints and their callbacks.
*/
class Rest_Api_Endpoints {
/**
* Declare the JITM's REST API endpoints.
*/
public static function register_endpoints() {
register_rest_route(
'jetpack/v4',
'/jitm',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => __CLASS__ . '::get_jitm_message',
'permission_callback' => '__return_true',
)
);
register_rest_route(
'jetpack/v4',
'/jitm',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => __CLASS__ . '::delete_jitm_message',
'permission_callback' => __CLASS__ . '::delete_jitm_message_permission_callback',
)
);
}
/**
* Asks for a jitm, unless they've been disabled, in which case it returns an empty array
*
* @param WP_REST_Request $request The request object.
*
* @return array An array of jitms
*/
public static function get_jitm_message( $request ) {
$jitm = JITM::get_instance();
if ( ! $jitm->jitms_enabled() ) {
return array();
}
return $jitm->get_messages( $request['message_path'], urldecode_deep( $request['query'] ), 'true' === $request['full_jp_logo_exists'] ? true : false );
}
/**
* Dismisses a jitm.
*
* @param WP_REST_Request $request The request object.
*
* @return bool Always True
*/
public static function delete_jitm_message( $request ) {
$jitm = JITM::get_instance();
if ( ! $jitm->jitms_enabled() ) {
return true;
}
return $jitm->dismiss( $request['id'], $request['feature_class'] );
}
/**
* Verify that the user can dismiss JITM messages.
*
* @return bool|WP_Error True if user is able to dismiss JITM messages.
*/
public static function delete_jitm_message_permission_callback() {
if ( current_user_can( 'read' ) ) {
return true;
}
return new \WP_Error( 'invalid_user_permission_jetpack_delete_jitm_message', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
}

View File

@ -0,0 +1,494 @@
// Just in Time Messaging - message prompts
// TODO: Switch to using the `@automattic/jetpack-base-styles` package when its ready.
@import '../../plugins/jetpack/_inc/client/scss/functions/rem';
@import '../../plugins/jetpack/_inc/client/scss/variables/colors';
@import '../../plugins/jetpack/_inc/client/scss/mixins/breakpoints';
@import '../../plugins/jetpack/_inc/client/scss/calypso-colors';
@mixin clear-fix {
&:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
}
@mixin jitm-banner-color( $color ) {
border-left-color: $color;
.jitm-banner__icon {
color: $color;
}
.jitm-banner__icon-circle {
background-color: $color;
}
}
// New JITMS - modified calypso banner styles
$blue-grey-light: #f6f7f7;
$blue-medium-dark: #2271b1;
.jitm-button {
background: $blue-grey-light;
border-color: $blue-medium-dark;
border-style: solid;
border-width: 1px;
color: $blue-medium-dark;
cursor: pointer;
display: inline-block;
margin: 0;
outline: 0;
overflow: hidden;
font-size: 14px;
text-overflow: ellipsis;
text-decoration: none;
vertical-align: top;
box-sizing: border-box;
font-size: 14px;
border-radius: 4px;
padding: 7px 14px 9px;
-webkit-appearance: none;
appearance: none;
text-align: center;
min-width: 90px;
&:hover {
background: #f0f0f1;
border-color: $blue-grey-dark;
color: $blue-grey-dark;
}
&[disabled],
&:disabled {
color: lighten( $gray, 30% );
background: $white;
border-color: lighten( $gray, 30% );
cursor: default;
}
&:focus {
background: $white;
border-color: $blue-medium-dark;
box-shadow: 0 0 0 1px $blue-medium-dark;
}
&.is-compact {
padding: 7px;
font-size: 11px;
line-height: 1;
text-transform: uppercase;
white-space: nowrap;
&:disabled {
color: lighten( $gray, 30% );
}
.gridicon {
top: 4px;
margin-top: -8px;
}
// Make the left margin of the small plus icon visually less huge
.gridicons-plus-small {
margin-left: -4px;
}
// Reset the left margin if the button contains only the plus icon
.gridicons-plus-small:last-of-type {
margin-left: 0;
}
// Make plus icon nudged closer to adjacent icons for add-people and add-plugin type buttons
.gridicons-plus-small + .gridicon {
margin-left: -4px;
}
}
&.hidden {
display: none;
}
}
// Primary buttons
.jitm-button.is-primary {
background: $blue-medium;
border-color: $blue-medium;
color: $white;
&:hover,
&:focus {
border-color: $blue-medium-dark;
background: $blue-medium-dark;
color: $white;
}
&:focus {
box-shadow:
0 0 0 1px $white,
0 0 0 3px $blue-medium-dark;
}
&[disabled],
&:disabled {
background: tint( $blue-light, 50% );
border-color: tint( $blue-wordpress, 55% );
color: $white;
}
&.is-compact {
color: $white;
white-space: nowrap;
}
}
.jitm-card {
display: block;
clear: both;
position: relative;
margin: rem( 48px ) rem( 20px ) 0 auto;
padding: rem( 16px );
box-sizing: border-box;
background: $white;
box-shadow:
0 0 0 1px $light-gray-700,
0 1px 1px 1px rgba(0,0,0,.04);
@include clear-fix;
@include breakpoint( ">480px" ) {
margin-bottom: rem( 16px );
padding: rem( 24px );
}
// Compact Card
&.is-compact {
margin-bottom: rem( 1px );
@include breakpoint( ">480px" ) {
margin-bottom: 1px;
padding: rem( 16px ) rem( 24px );
}
}
&.is-card-link {
padding-right: rem( 48px );
}
}
// Minor adjustments for the display in My Jetpack.
.my-jetpack-jitm-card {
.jitm-card {
margin-right: 0;
margin-bottom: 0;
}
}
// if JITM appears directly below WordPress "help" menu adjust margins
#screen-meta-links+.jitm-card {
margin: rem( 40px ) 1.5385em 0 auto;
}
// if JITM appears directly below WordPress hello dolly adjust margins
#dolly+.jitm-card {
margin: 3rem 1rem 0 auto;
}
// remove right margin for jitms in the editor
.post-php .jitm-card {
margin-right: 0;
}
// if JITM appears inside of the jetpack dashboard adjust margins
.jp-lower .jitm-card {
margin: 0 0 rem( 24px );
}
.jitm-banner.jitm-card {
border-left: 4px solid;
display: flex;
padding: rem( 12px ) rem( 6px ) rem( 12px ) rem( 12px );
position: relative;
z-index: 2;
@include breakpoint( "<480px" ) {
display: flex;
padding: rem( 12px );
flex-direction: column;
}
&.is-card-link {
padding: rem( 12px ) rem( 48px ) rem( 12px ) rem( 16px );
}
&.is-dismissible {
padding-right: rem( 48px );
}
@include jitm-banner-color( $alert-green );
&.is-upgrade-personal {
@include jitm-banner-color( $alert-yellow );
}
&.is-upgrade-premium {
@include jitm-banner-color( $alert-green );
}
&.is-upgrade-business,
&.woo-jitm {
@include jitm-banner-color( $alert-purple );
}
.jitm-card__link-indicator {
align-items: center;
color: $blue-wordpress;
display: flex;
}
&:hover {
transition: all 100ms ease-in-out;
&.is-card-link {
box-shadow: 0 0 0 1px $gray, 0 2px 4px lighten( $gray, 20% );
}
.jitm-card__link-indicator {
color: $blue-dark;
}
}
@include breakpoint( ">480px" ) {
padding: rem( 12px ) rem( 16px );
&.is-dismissible {
padding-right: rem( 16px );
}
}
}
.jitm-banner__buttons_container {
display: grid;
height: 50%;
margin-top: auto;
margin-bottom: auto;
@include breakpoint( ">480px" ) {
display: flex;
}
}
.jitm-banner__icons {
display: flex;
.jitm-banner__icon,
.jitm-banner__icon-circle {
border-radius: 50%;
flex-shrink: 0;
height: rem( 24px );
width: rem( 24px );
margin-right: rem( 16px );
margin-top: rem( -2px );
text-align: center;
top: rem( 4px );
}
.jitm-banner__icon {
align-self: center;
color: $white;
display: block;
}
.jitm-banner__icon-circle {
color: white;
display: none;
padding: rem( 3px ) rem( 4px ) rem( 4px ) rem( 3px );
}
@include breakpoint( ">480px" ) {
align-items: center;
.jitm-banner__icon {
display: none;
}
.jitm-banner__icon-circle {
display: block;
}
}
}
.jitm-banner__icon-plan {
display: flex;
margin-right: rem( 16px );
.dops-plan-icon {
height: rem( 32px );
width: rem( 32px );
}
.jp-emblem {
position: relative;
top: rem( 2px );
@include breakpoint( "<480px" ) {
margin-bottom: rem( 12px );
}
svg {
height: rem( 32px );
width: rem( 32px );
fill: $green-primary;
}
.jitm-jp-logo {
height: inherit;
width: 6rem;
fill: inherit;
}
}
@include breakpoint( ">480px" ) {
align-items: center;
}
@include breakpoint( "<960px" ) {
margin-bottom: 10px;
}
}
.jitm-banner__content {
align-items: center;
display: flex;
flex-grow: 1;
flex-wrap: wrap;
@include breakpoint( "<480px" ) {
margin-right: 0;
}
@include breakpoint( ">480px" ) {
flex-wrap: nowrap;
}
@include breakpoint( "<960px" ) {
display: grid;
margin-right: 5px;
}
}
.jitm-banner__info {
flex-grow: 1;
line-height: 1.4;
@include breakpoint( ">480px" ) {
flex-basis: 50%;
}
@include breakpoint( ">960px" ) {
flex-basis: 70%;
}
.jitm-banner__title,
.jitm-banner__description {
color: $gray-dark;
}
.jitm-banner__title {
font-size: 14px;
font-weight: 500;
}
.jitm-banner__description {
font-size: rem( 12px );
line-height: 1.5;
margin-top: rem( 6px );
}
.banner__list {
font-size: 12px;
list-style: none;
margin: 10px 0;
li {
margin: 6px 0;
.gridicon {
fill: $gray;
display: inline;
margin-right: 12px;
vertical-align: bottom;
}
}
}
}
.jitm-banner__action {
align-self: center;
font-size: rem( 12px );
margin: rem( 8px ) 0 0;
text-align: left;
width: 100%;
.jitm-banner__prices {
display: flex;
justify-content: flex-start;
.dops-plan-price {
margin-bottom: 0;
}
.dops-plan-price.is-discounted,
.dops-plan-price.is-discounted .dops-plan-price__currency-symbol {
color: $gray-dark;
}
.has-call-to-action & .dops-plan-price {
margin-bottom: rem( 8px );
}
}
@include breakpoint( "<480px" ) {
margin-top: 1rem;
}
@include breakpoint( ">480px" ) {
margin: 0 rem( 4px ) 0 rem( 8px );
text-align: center;
width: auto;
.is-dismissible {
margin-top: rem( 40px );
}
.jitm-banner__prices {
justify-content: flex-end;
text-align: right;
}
}
}
.jitm-banner__dismiss {
display: block;
text-decoration: none;
line-height: .5;
margin-top: auto;
margin-bottom: auto;
&:before {
color: darken($gray, 20%);
font: 400 16px/1 dashicons;
content: '\f158';
}
@include breakpoint( ">660px" ) {
margin-right: rem( -8px );
}
@include breakpoint( "<480px" ) {
position: absolute;
top: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
// Minimum touch area
width: 48px;
height: 48px;
}
}
.jitm-banner__action + .jitm-banner__dismiss {
margin-left: rem( 10px );
}
#dolly + .jitm-card {
margin: 3rem 1rem 0 auto;
}

View File

@ -0,0 +1,269 @@
import '../css/jetpack-admin-jitm.scss';
jQuery( document ).ready( function ( $ ) {
var templates = {
default: function ( envelope ) {
var html =
'<div class="jitm-card jitm-banner ' +
( envelope.CTA.message ? 'has-call-to-action' : '' ) +
' is-upgrade-premium ' +
envelope.content.classes +
'" data-stats_url="' +
envelope.jitm_stats_url +
'">';
html += '<div class="jitm-banner__content">';
html += '<div class="jitm-banner__icon-plan">' + envelope.content.icon + '</div>';
html += '<div class="jitm-banner__info">';
html += '<div class="jitm-banner__title">' + envelope.content.message + '</div>';
if ( envelope.content.description && envelope.content.description !== '' ) {
html += '<div class="jitm-banner__description">' + envelope.content.description;
if ( envelope.content.list.length > 0 ) {
html += '<ul class="banner__list">';
for ( var i = 0; i < envelope.content.list.length; i++ ) {
var text = envelope.content.list[ i ].item;
if ( envelope.content.list[ i ].url ) {
text =
'<a href="' +
envelope.content.list[ i ].url +
'" target="_blank" rel="noopener noreferrer" data-module="' +
envelope.feature_class +
'" data-jptracks-name="nudge_item_click" data-jptracks-prop="jitm-' +
envelope.id +
'">' +
text +
'</a>';
}
html +=
'<li>' +
'<svg class="gridicon gridicons-checkmark" height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g>' +
'<path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414" /></g></svg>' +
text +
'</li>';
}
}
html += '</div>';
}
html += '</div>';
html += '</div>';
html += '<div class="jitm-banner__buttons_container">';
if ( envelope.activate_module ) {
html += '<div class="jitm-banner__action" id="jitm-banner__activate">';
html +=
'<a href="#" data-module="' +
envelope.activate_module +
'" type="button" class="jitm-button is-compact is-primary jptracks" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-' +
envelope.id +
'-activate_module">' +
window.jitm_config.activate_module_text +
'</a>';
html += '</div>';
}
if ( envelope.CTA.message ) {
var ctaClasses = 'jitm-button is-compact jptracks';
if ( envelope.CTA.primary && null === envelope.activate_module ) {
ctaClasses += ' is-primary';
}
var ajaxAction = envelope.CTA.ajax_action;
html += '<div class="jitm-banner__action">';
html +=
'<a href="' +
( envelope.CTA.hasOwnProperty( 'link' ) && envelope.CTA.link.length
? envelope.CTA.link
: envelope.url ) +
'" target="' +
( envelope.CTA.newWindow === false || ajaxAction ? '_self' : '_blank' ) +
'" rel="noopener noreferrer" title="' +
envelope.CTA.message +
'" data-module="' +
envelope.feature_class +
'" type="button" class="' +
ctaClasses +
'" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-' +
envelope.id +
'" ' +
( ajaxAction ? 'data-ajax-action="' + ajaxAction + '"' : '' ) +
'>' +
envelope.CTA.message +
'</a>';
html += '</div>';
}
html += '</div>';
if ( envelope.is_dismissible ) {
html +=
'<a href="#" data-module="' +
envelope.feature_class +
'" class="jitm-banner__dismiss"></a>';
}
html += '</div>';
return $( html );
},
};
var setJITMContent = function ( $el, response, redirect ) {
var template;
var render = function ( $my_template ) {
return function ( e ) {
e.preventDefault();
$my_template.hide();
$.ajax( {
url: window.jitm_config.api_root + 'jetpack/v4/jitm',
method: 'POST', // using DELETE without permalinks is broken in default nginx configuration
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', window.jitm_config.nonce );
},
data: {
id: response.id,
feature_class: response.feature_class,
},
} );
};
};
template = response.template;
// if we don't have a template for this version, just use the default template
if ( ! template || ! templates[ template ] ) {
template = 'default';
}
response.url = response.url + '&redirect=' + redirect;
var $template = templates[ template ]( response );
$template.find( '.jitm-banner__dismiss' ).on( 'click', render( $template ) );
if ( $( '#jp-admin-notices' ).length > 0 ) {
// Add to Jetpack notices within the Jetpack settings app.
$el.innerHTML = $template;
// If we already have a message, replace it.
if ( $( '#jp-admin-notices' ).find( '.jitm-card' ) ) {
$( '.jitm-card' ).replaceWith( $template );
}
// No existing JITM? Add ours to the top of the Jetpack admin notices.
$template.prependTo( $( '#jp-admin-notices' ) );
} else {
// Replace placeholder div on other pages.
$el.replaceWith( $template );
}
// Handle Module activation button if it exists.
$template.find( '#jitm-banner__activate a' ).on( 'click', function () {
var $activate_button = $( this );
// Do not allow any requests if the button is disabled.
if ( $activate_button.attr( 'disabled' ) ) {
return false;
}
// Make request to activate module.
$.ajax( {
url:
window.jitm_config.api_root +
'jetpack/v4/module/' +
$activate_button.data( 'module' ) +
'/active',
method: 'POST',
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', $el.data( 'nonce' ) );
// Change the button status to disabled as the change is in progress.
$( '#jitm-banner__activate a' ).text( window.jitm_config.activating_module_text );
$( '#jitm-banner__activate a' ).attr( 'disabled', true );
},
} ).done( function () {
$( '#jitm-banner__activate a' ).text( window.jitm_config.activated_module_text );
$( '#jitm-banner__activate a' ).attr( 'disabled', true );
// Hide the JITM after 2 seconds.
setTimeout( function () {
$template.fadeOut( 'slow' );
}, 2000 );
} );
} );
// Handle CTA ajax actions.
$template.find( '.jitm-button[data-ajax-action]' ).on( 'click', function ( e ) {
e.preventDefault();
var button = $( this );
button.attr( 'disabled', true );
$.post( window.ajaxurl, {
action: button.data( 'ajax-action' ),
_nonce: $el.data( 'ajax-nonce' ),
} )
.done( function () {
$template.fadeOut( 'slow' );
} )
.fail( function () {
button.attr( 'disabled', false );
} );
return false;
} );
};
var reFetch = function () {
$( '.jetpack-jitm-message' ).each( function () {
var $el = $( this );
var message_path = $el.data( 'message-path' );
var query = $el.data( 'query' );
var redirect = $el.data( 'redirect' );
var hash = location.hash;
hash = hash.replace( /#\//, '_' );
if ( '_dashboard' !== hash ) {
message_path = message_path.replace(
'toplevel_page_jetpack',
'toplevel_page_jetpack' + hash
);
}
var full_jp_logo_exists = $( '.jetpack-logo__masthead' ).length ? true : false;
$.get( window.jitm_config.api_root + 'jetpack/v4/jitm', {
message_path: message_path,
query: query,
full_jp_logo_exists: full_jp_logo_exists,
_wpnonce: $el.data( 'nonce' ),
} ).then( function ( response ) {
if ( 'object' === typeof response && response[ '1' ] ) {
response = [ response[ '1' ] ];
}
// properly handle the case of an empty array or no content set
if ( 0 === response.length || ! response[ 0 ].content ) {
return;
}
// for now, always take the first response
setJITMContent( $el, response[ 0 ], redirect );
} );
} );
};
reFetch();
$( window ).on( 'hashchange', function ( e ) {
var newURL = e.originalEvent.newURL;
if ( newURL.indexOf( 'jetpack#/' ) >= 0 ) {
var jitm_card = document.querySelector( '.jitm-card' );
if ( jitm_card ) {
jitm_card.remove();
}
reFetch();
}
} );
} );