init
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Disable direct access/execution to/of the widget code.
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* Widget to display blog authors with avatars and recent posts.
|
||||
*
|
||||
* Configurable parameters include:
|
||||
* 1. Whether to display authors who haven't written any posts
|
||||
* 2. The number of posts to be displayed per author (defaults to 0)
|
||||
* 3. Avatar size
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
class Jetpack_Widget_Authors extends WP_Widget {
|
||||
/**
|
||||
* Jetpack_Widget_Authors contructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'authors',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Authors', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_authors',
|
||||
'description' => __( 'Display blogs authors with avatars and recent posts.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
add_action( 'publish_post', array( __CLASS__, 'flush_cache' ) );
|
||||
add_action( 'deleted_post', array( __CLASS__, 'flush_cache' ) );
|
||||
add_action( 'switch_theme', array( __CLASS__, 'flush_cache' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheet to adapt the widget to various themes.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_register_style( 'jetpack-authors-widget', plugins_url( 'authors/style.css', __FILE__ ), array(), '20161228' );
|
||||
wp_enqueue_style( 'jetpack-authors-widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush Authors widget cached data.
|
||||
*/
|
||||
public static function flush_cache() {
|
||||
wp_cache_delete( 'widget_authors', 'widget' );
|
||||
wp_cache_delete( 'widget_authors_ssl', 'widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes the widget content.
|
||||
*
|
||||
* @param array $args Display arguments.
|
||||
* @param array $instance Widget settings for the instance.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
$cache_bucket = is_ssl() ? 'widget_authors_ssl' : 'widget_authors';
|
||||
|
||||
if ( '%BEG_OF_TITLE%' !== $args['before_title'] ) {
|
||||
$output = wp_cache_get( $cache_bucket, 'widget' );
|
||||
if ( $output ) {
|
||||
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Cached widget display.
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
}
|
||||
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => __( 'Authors', 'jetpack' ),
|
||||
'all' => false,
|
||||
'number' => 5,
|
||||
'avatar_size' => 48,
|
||||
)
|
||||
);
|
||||
$instance['number'] = min( 10, max( 0, (int) $instance['number'] ) );
|
||||
|
||||
// We need to query at least one post to determine whether an author has written any posts or not.
|
||||
$query_number = max( $instance['number'], 1 );
|
||||
|
||||
/**
|
||||
* Filter authors from the Widget Authors widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @deprecated 7.7.0 Use jetpack_widget_authors_params instead.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param array $default_excluded_authors Array of user ID's that will be excluded
|
||||
*/
|
||||
$excluded_authors = apply_filters( 'jetpack_widget_authors_exclude', array() );
|
||||
|
||||
/**
|
||||
* Filter the parameters of `get_users` call in the Widget Authors widget.
|
||||
*
|
||||
* See the following for `get_users` default arguments:
|
||||
* https://codex.wordpress.org/Function_Reference/get_users
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 7.7.0
|
||||
*
|
||||
* @param array $get_author_params Array of params used in `get_user`
|
||||
*/
|
||||
$get_author_params = apply_filters(
|
||||
'jetpack_widget_authors_params',
|
||||
array(
|
||||
'capability' => array( 'edit_posts' ),
|
||||
'exclude' => (array) $excluded_authors,
|
||||
)
|
||||
);
|
||||
|
||||
$authors = get_users( $get_author_params );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<ul>';
|
||||
|
||||
$default_post_type = 'post';
|
||||
/**
|
||||
* Filter types of posts that will be counted in the widget
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string|array $default_post_type type(s) of posts to count for the widget.
|
||||
*/
|
||||
$post_types = apply_filters( 'jetpack_widget_authors_post_types', $default_post_type );
|
||||
|
||||
foreach ( $authors as $author ) {
|
||||
$r = new WP_Query(
|
||||
array(
|
||||
'author' => $author->ID,
|
||||
'posts_per_page' => $query_number,
|
||||
'post_type' => $post_types,
|
||||
'post_status' => 'publish',
|
||||
'no_found_rows' => true,
|
||||
'has_password' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $r->have_posts() && ! $instance['all'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo '<li>';
|
||||
|
||||
// Display avatar and author name.
|
||||
if ( $r->have_posts() ) {
|
||||
echo '<a href="' . esc_url( get_author_posts_url( $author->ID ) ) . '">';
|
||||
|
||||
if ( $instance['avatar_size'] > 1 ) {
|
||||
echo ' ' . get_avatar( $author->ID, $instance['avatar_size'], '', true ) . ' ';
|
||||
}
|
||||
|
||||
echo '<strong>' . esc_html( $author->display_name ) . '</strong>';
|
||||
echo '</a>';
|
||||
} elseif ( $instance['all'] ) {
|
||||
if ( $instance['avatar_size'] > 1 ) {
|
||||
echo get_avatar( $author->ID, $instance['avatar_size'], '', true ) . ' ';
|
||||
}
|
||||
|
||||
echo '<strong>' . esc_html( $author->display_name ) . '</strong>';
|
||||
}
|
||||
|
||||
if ( 0 === (int) $instance['number'] ) {
|
||||
echo '</li>';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Display a short list of recent posts for this author.
|
||||
if ( $r->have_posts() ) {
|
||||
echo '<ul>';
|
||||
|
||||
while ( $r->have_posts() ) {
|
||||
$r->the_post();
|
||||
|
||||
printf(
|
||||
'<li><a href="%1$s" title="%2$s"%3$s>%4$s</a></li>',
|
||||
esc_url( get_permalink() ),
|
||||
esc_attr( wp_kses( get_the_title(), array() ) ),
|
||||
( get_queried_object_id() === get_the_ID() ? ' aria-current="page"' : '' ),
|
||||
esc_html( wp_kses( get_the_title(), array() ) )
|
||||
);
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
if ( '%BEG_OF_TITLE%' !== $args['before_title'] ) {
|
||||
wp_cache_add( $cache_bucket, ob_get_flush(), 'widget' );
|
||||
}
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'authors' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget settings form.
|
||||
*
|
||||
* @param array $instance Current settings.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => '',
|
||||
'all' => false,
|
||||
'avatar_size' => 48,
|
||||
'number' => 5,
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Title:', 'jetpack' ); ?>
|
||||
<input class="widefat" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<input class="checkbox" type="checkbox" <?php checked( $instance['all'] ); ?> name="<?php echo esc_attr( $this->get_field_name( 'all' ) ); ?>" />
|
||||
<?php esc_html_e( 'Display all authors (including those who have not written any posts)', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Number of posts to show for each author:', 'jetpack' ); ?>
|
||||
<input style="width: 50px; text-align: center;" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['number'] ); ?>" />
|
||||
<?php esc_html_e( '(at most 10)', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Avatar Size (px):', 'jetpack' ); ?>
|
||||
<select name="<?php echo esc_attr( $this->get_field_name( 'avatar_size' ) ); ?>">
|
||||
<?php
|
||||
foreach ( array(
|
||||
'1' => __( 'No Avatars', 'jetpack' ),
|
||||
'16' => '16x16',
|
||||
'32' => '32x32',
|
||||
'48' => '48x48',
|
||||
'96' => '96x96',
|
||||
'128' => '128x128',
|
||||
) as $value => $label ) {
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $instance['avatar_size'] ); ?>><?php echo esc_html( $label ); ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the widget on save and flushes cache.
|
||||
*
|
||||
* @param array $new_instance New widget instance data.
|
||||
* @param array $old_instance Old widget instance data.
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$new_instance['title'] = wp_strip_all_tags( $new_instance['title'] );
|
||||
$new_instance['all'] = isset( $new_instance['all'] ) ? (bool) $new_instance['all'] : false;
|
||||
$new_instance['number'] = (int) $new_instance['number'];
|
||||
$new_instance['avatar_size'] = (int) $new_instance['avatar_size'];
|
||||
|
||||
self::flush_cache();
|
||||
|
||||
return $new_instance;
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_register_widget_authors' );
|
||||
/**
|
||||
* Register the Authors widget.
|
||||
*/
|
||||
function jetpack_register_widget_authors() {
|
||||
register_widget( 'Jetpack_Widget_Authors' );
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Authors Widget */
|
||||
.widget_authors > ul, .widget.widget_authors li > ul {
|
||||
margin-left: inherit;
|
||||
padding-left: 0;
|
||||
}
|
||||
.widget_authors ul li li {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.widget_authors > ul > li {
|
||||
margin-bottom: 1em;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.widget_authors > ul > li + li {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.widget.widget_authors img {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileNam
|
||||
/**
|
||||
* Blog Stats Widget.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Stats\WPCOM_Stats;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
|
||||
// Disable direct access/execution to/of the widget code.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Blog Stats Widget.
|
||||
*
|
||||
* Displays all time stats for that site.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
class Jetpack_Blog_Stats_Widget extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'blog-stats',
|
||||
'description' => esc_html__( 'Show a hit counter for your blog.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct(
|
||||
'blog-stats',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Blog Stats', 'jetpack' ) ),
|
||||
$widget_ops
|
||||
);
|
||||
$this->alt_option_name = 'widget_statscounter';
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Blog Stats" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
// @TODO: Hide for Simple sites when the block API starts working.
|
||||
if ( ! ( new Host() )->is_wpcom_simple() ) {
|
||||
$widget_types[] = 'blog-stats';
|
||||
}
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Array of default values for the Widget's options
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'title' => esc_html__( 'Blog Stats', 'jetpack' ),
|
||||
/* Translators: Number of views, plural */
|
||||
'hits' => esc_html__( 'hits', 'jetpack' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return All Time Stats for that blog.
|
||||
*
|
||||
* We query the WordPress.com Stats REST API endpoint.
|
||||
*
|
||||
* @uses Automattic\Jetpack\Stats\WPCOM_Stats->get_stats. That function caches data locally for 5 minutes.
|
||||
*
|
||||
* @return string|false $views All Time Stats for that blog.
|
||||
*/
|
||||
public function get_stats() {
|
||||
$wpcom_stats = new WPCOM_Stats();
|
||||
// Get data from the WordPress.com Stats REST API endpoint.
|
||||
$stats = $wpcom_stats->convert_stats_array_to_object( $wpcom_stats->get_stats( array( 'fields' => 'stats' ) ) );
|
||||
|
||||
if ( isset( $stats->stats->views ) ) {
|
||||
return $stats->stats->views;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Back end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'hits' ) ); ?>"><?php esc_html_e( 'Pageview Description:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'hits' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'hits' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['hits'] ); ?>" />
|
||||
</p>
|
||||
<p><?php esc_html_e( 'Hit counter is delayed by up to 5 minutes.', 'jetpack' ); ?></p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
$instance['hits'] = wp_kses( $new_instance['hits'], array() );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
// Get the Site Stats.
|
||||
$views = $this->get_stats();
|
||||
|
||||
if ( 0 === $views ) {
|
||||
esc_html_e( 'There is nothing to display yet', 'jetpack' );
|
||||
} elseif ( $views ) {
|
||||
printf(
|
||||
'<ul><li>%1$s %2$s</li></ul>',
|
||||
esc_html( number_format_i18n( $views ) ),
|
||||
isset( $instance['hits'] ) ? esc_html( $instance['hits'] ) : ''
|
||||
);
|
||||
} else {
|
||||
esc_html_e( 'There was an issue retrieving stats. Please try again later.', 'jetpack' );
|
||||
}
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'blog_stats' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the Stats module is active in a recent version of Jetpack, register the widget.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
function jetpack_blog_stats_widget_init() {
|
||||
if ( Jetpack::is_module_active( 'stats' ) ) {
|
||||
register_widget( 'Jetpack_Blog_Stats_Widget' );
|
||||
}
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_blog_stats_widget_init' );
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
/**
|
||||
* Main class file for EU Cookie Law Widget.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
|
||||
/**
|
||||
* Disable direct access/execution to/of the widget code.
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Jetpack_EU_Cookie_Law_Widget' ) ) {
|
||||
/**
|
||||
* EU Cookie Law Widget
|
||||
*
|
||||
* Display the EU Cookie Law banner in the bottom part of the screen.
|
||||
*/
|
||||
class Jetpack_EU_Cookie_Law_Widget extends WP_Widget {
|
||||
/**
|
||||
* EU Cookie Law cookie name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $cookie_name = 'eucookielaw';
|
||||
|
||||
/**
|
||||
* Default hide options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $hide_options = array(
|
||||
'button',
|
||||
'scroll',
|
||||
'time',
|
||||
);
|
||||
|
||||
/**
|
||||
* Default text options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $text_options = array(
|
||||
'default',
|
||||
'custom',
|
||||
);
|
||||
|
||||
/**
|
||||
* Default color scheme options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $color_scheme_options = array(
|
||||
'default',
|
||||
'negative',
|
||||
);
|
||||
|
||||
/**
|
||||
* Default policy URL options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $policy_url_options = array(
|
||||
'default',
|
||||
'custom',
|
||||
);
|
||||
|
||||
/**
|
||||
* Widget position options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $position_options = array(
|
||||
'bottom',
|
||||
'top',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'eu_cookie_law_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Cookies & Consents Banner', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => esc_html__( 'Display a banner for EU Cookie Law and GDPR compliance.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
),
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles.
|
||||
*/
|
||||
public function enqueue_frontend_scripts() {
|
||||
wp_enqueue_style( 'eu-cookie-law-style', plugins_url( 'eu-cookie-law/style.css', __FILE__ ), array(), JETPACK__VERSION );
|
||||
|
||||
if ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) {
|
||||
wp_enqueue_script(
|
||||
'eu-cookie-law-script',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/eu-cookie-law/eu-cookie-law.min.js',
|
||||
'modules/widgets/eu-cookie-law/eu-cookie-law.js'
|
||||
),
|
||||
array(),
|
||||
'20180522',
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values.
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Default values for the widget options.
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'hide' => $this->hide_options[0],
|
||||
'hide-timeout' => 30,
|
||||
'consent-expiration' => 180,
|
||||
'text' => $this->text_options[0],
|
||||
'customtext' => '',
|
||||
'color-scheme' => $this->color_scheme_options[0],
|
||||
'policy-url' => get_option( 'wp_page_for_privacy_policy' ) ? $this->policy_url_options[1] : $this->policy_url_options[0],
|
||||
'default-policy-url' => 'https://automattic.com/cookies/',
|
||||
'custom-policy-url' => get_option( 'wp_page_for_privacy_policy' ) ? get_permalink( (int) get_option( 'wp_page_for_privacy_policy' ) ) : '',
|
||||
'position' => $this->position_options[0],
|
||||
'policy-link-text' => esc_html__( 'Cookie Policy', 'jetpack' ),
|
||||
'button' => esc_html__( 'Close and accept', 'jetpack' ),
|
||||
'default-text' => esc_html__( "Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use. \r\nTo find out more, including how to control cookies, see here:", 'jetpack' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of the widget.
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
* @html-template-var array<string,string> $classes
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
/**
|
||||
* Filters the display of the EU Cookie Law widget.
|
||||
*
|
||||
* @since 6.1.1
|
||||
*
|
||||
* @param bool true Should the EU Cookie Law widget be disabled. Default to false.
|
||||
*/
|
||||
if ( apply_filters( 'jetpack_disable_eu_cookie_law_widget', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_frontend_scripts();
|
||||
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
|
||||
require __DIR__ . '/eu-cookie-law/widget-amp.php';
|
||||
return;
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
$classes['hide'] = 'hide-on-' . esc_attr( $instance['hide'] );
|
||||
if ( 'negative' === $instance['color-scheme'] ) {
|
||||
$classes['negative'] = 'negative';
|
||||
}
|
||||
|
||||
if ( 'top' === $instance['position'] ) {
|
||||
$classes['top'] = 'top';
|
||||
}
|
||||
|
||||
if ( Jetpack::is_module_active( 'wordads' ) ) {
|
||||
$classes['ads'] = 'ads-active';
|
||||
$classes['hide'] = 'hide-on-button';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if widget is loaded in widgets.php.
|
||||
*
|
||||
* @return string widget Static version of the widget for better preview.
|
||||
*/
|
||||
global $pagenow;
|
||||
if ( 'widgets.php' === $pagenow ) {
|
||||
// To prevent the widget from being added as a pop-up
|
||||
// we do not echo the before and after $args. Instead we wrap
|
||||
// it in a dummy `div` and return before the `widget_view` is
|
||||
// added to stats.
|
||||
echo '<div id="eu-cookie-law" style="padding: 0;margin: 5px">';
|
||||
require_once __DIR__ . '/eu-cookie-law/widget.php';
|
||||
echo '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
require_once __DIR__ . '/eu-cookie-law/widget.php';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'eu_cookie_law' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-end widget form.
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
if ( Jetpack::is_module_active( 'wordads' ) ) {
|
||||
$instance['hide'] = 'button';
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'eu-cookie-law-widget-admin',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/eu-cookie-law/eu-cookie-law-admin.min.js',
|
||||
'modules/widgets/eu-cookie-law/eu-cookie-law-admin.js'
|
||||
),
|
||||
array( 'jquery' ),
|
||||
20180417,
|
||||
false
|
||||
);
|
||||
|
||||
require __DIR__ . '/eu-cookie-law/form.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$defaults = $this->defaults();
|
||||
|
||||
$instance['hide'] = $this->filter_value( isset( $new_instance['hide'] ) ? $new_instance['hide'] : '', $this->hide_options );
|
||||
$instance['text'] = $this->filter_value( isset( $new_instance['text'] ) ? $new_instance['text'] : '', $this->text_options );
|
||||
$instance['color-scheme'] = $this->filter_value( isset( $new_instance['color-scheme'] ) ? $new_instance['color-scheme'] : '', $this->color_scheme_options );
|
||||
$instance['policy-url'] = $this->filter_value( isset( $new_instance['policy-url'] ) ? $new_instance['policy-url'] : '', $this->policy_url_options );
|
||||
$instance['position'] = $this->filter_value( isset( $new_instance['position'] ) ? $new_instance['position'] : '', $this->position_options );
|
||||
|
||||
if ( isset( $new_instance['hide-timeout'] ) ) {
|
||||
// Time can be a value between 3 and 1000 seconds.
|
||||
$instance['hide-timeout'] = min( 1000, max( 3, (int) $new_instance['hide-timeout'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['consent-expiration'] ) ) {
|
||||
// Time can be a value between 1 and 365 days.
|
||||
$instance['consent-expiration'] = min( 365, max( 1, (int) $new_instance['consent-expiration'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['customtext'] ) ) {
|
||||
$instance['customtext'] = mb_substr( wp_kses( $new_instance['customtext'], array() ), 0, 4096 );
|
||||
} else {
|
||||
$instance['text'] = $this->text_options[0];
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['policy-url'] ) ) {
|
||||
$instance['policy-url'] = 'custom' === $new_instance['policy-url']
|
||||
? 'custom'
|
||||
: 'default';
|
||||
} else {
|
||||
$instance['policy-url'] = $this->policy_url_options[0];
|
||||
}
|
||||
|
||||
if ( 'custom' === $instance['policy-url'] && isset( $new_instance['custom-policy-url'] ) ) {
|
||||
$instance['custom-policy-url'] = esc_url( $new_instance['custom-policy-url'], array( 'http', 'https' ) );
|
||||
|
||||
if ( strlen( $instance['custom-policy-url'] ) < 10 ) {
|
||||
unset( $instance['custom-policy-url'] );
|
||||
global $wp_customize;
|
||||
if ( ! isset( $wp_customize ) ) {
|
||||
$instance['policy-url'] = $this->policy_url_options[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['policy-link-text'] ) ) {
|
||||
$instance['policy-link-text'] = trim( mb_substr( wp_kses( $new_instance['policy-link-text'], array() ), 0, 100 ) );
|
||||
}
|
||||
|
||||
if ( empty( $instance['policy-link-text'] ) || $instance['policy-link-text'] === $defaults['policy-link-text'] ) {
|
||||
unset( $instance['policy-link-text'] );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['button'] ) ) {
|
||||
$instance['button'] = trim( mb_substr( wp_kses( $new_instance['button'], array() ), 0, 100 ) );
|
||||
}
|
||||
|
||||
if ( empty( $instance['button'] ) || $instance['button'] === $defaults['button'] ) {
|
||||
unset( $instance['button'] );
|
||||
}
|
||||
|
||||
// Show the banner again if a setting has been changed.
|
||||
setcookie( self::$cookie_name, '', time() - 86400, '/', COOKIE_DOMAIN, is_ssl(), false ); // phpcs:ignore Jetpack.Functions.SetCookie -- Fine to have accessible.
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is allowed and not empty.
|
||||
*
|
||||
* @param string $value Value to check.
|
||||
* @param array $allowed Array of allowed values.
|
||||
*
|
||||
* @return string $value if pass the check or first value from allowed values.
|
||||
*/
|
||||
public function filter_value( $value, $allowed = array() ) {
|
||||
$allowed = (array) $allowed;
|
||||
if ( empty( $value ) || ( ! empty( $allowed ) && ! in_array( $value, $allowed, true ) ) ) {
|
||||
$value = $allowed[0];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move these functions to some other file.
|
||||
|
||||
/**
|
||||
* Register Jetpack_EU_Cookie_Law_Widget widget.
|
||||
*/
|
||||
function jetpack_register_eu_cookie_law_widget() {
|
||||
register_widget( 'Jetpack_EU_Cookie_Law_Widget' );
|
||||
}
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_register_eu_cookie_law_widget' );
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
<?php
|
||||
/**
|
||||
* Instagram Widget. Display some Instagram photos via a widget.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager;
|
||||
|
||||
/**
|
||||
* This is the actual Instagram widget along with other code that only applies to the widget.
|
||||
*/
|
||||
class Jetpack_Instagram_Widget extends WP_Widget {
|
||||
|
||||
const ID_BASE = 'wpcom_instagram_widget'; // Don't change this as Atomic widgets will break.
|
||||
|
||||
/**
|
||||
* Options for the widget.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $valid_options;
|
||||
|
||||
/**
|
||||
* Default settings for the widgets.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $defaults;
|
||||
|
||||
/**
|
||||
* Sets the widget properties in WordPress, hooks a few functions, and sets some widget options.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
self::ID_BASE,
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Instagram', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'Display your latest Instagram photos.', 'jetpack' ),
|
||||
'show_instance_in_rest' => true,
|
||||
)
|
||||
);
|
||||
|
||||
add_action( 'wp_ajax_wpcom_instagram_widget_update_widget_token_id', array( $this, 'ajax_update_widget_token_id' ) );
|
||||
|
||||
$this->valid_options = array(
|
||||
/**
|
||||
* Allow changing the maximum number of columns available for the Instagram widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 8.8.0
|
||||
*
|
||||
* @param int $max_columns maximum number of columns.
|
||||
*/
|
||||
'max_columns' => apply_filters( 'wpcom_instagram_widget_max_columns', 3 ),
|
||||
'max_count' => 20,
|
||||
);
|
||||
|
||||
$this->defaults = array(
|
||||
'token_id' => null,
|
||||
'title' => __( 'Instagram', 'jetpack' ),
|
||||
'columns' => 2,
|
||||
'count' => 6,
|
||||
);
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Instagram" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = self::ID_BASE;
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the widget's frontend CSS but only if the widget is currently in use.
|
||||
*/
|
||||
public function enqueue_css() {
|
||||
wp_enqueue_style( self::ID_BASE, plugins_url( 'instagram/instagram.css', __FILE__ ), array(), JETPACK__VERSION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the widget's option in the database to have the passed Keyring token ID.
|
||||
* This is so the user doesn't have to click the "Save" button when we want to set it.
|
||||
*
|
||||
* @param int $token_id A Keyring token ID.
|
||||
* @param int $number The widget ID.
|
||||
*/
|
||||
public function update_widget_token_id( $token_id, $number = null ) {
|
||||
$widget_options = $this->get_settings();
|
||||
|
||||
if ( empty( $number ) ) {
|
||||
$number = $this->number;
|
||||
}
|
||||
|
||||
if ( ! isset( $widget_options[ $number ] ) || ! is_array( $widget_options[ $number ] ) ) {
|
||||
$widget_options[ $number ] = $this->defaults;
|
||||
}
|
||||
|
||||
$widget_options[ $number ]['token_id'] = (int) $token_id;
|
||||
|
||||
$this->save_settings( $widget_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the widget's option in the database to have the passed Keyring token ID.
|
||||
*
|
||||
* Sends a json success or error response.
|
||||
*/
|
||||
public function ajax_update_widget_token_id() {
|
||||
if ( ! check_ajax_referer( 'instagram-widget-save-token', 'savetoken', false ) ) {
|
||||
wp_send_json_error( array( 'message' => 'bad_nonce' ), 403 );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'customize' ) ) {
|
||||
wp_send_json_error( array( 'message' => 'not_authorized' ), 403 );
|
||||
}
|
||||
|
||||
$token_id = ! empty( $_POST['keyring_id'] ) ? (int) $_POST['keyring_id'] : null;
|
||||
$widget_id = ! empty( $_POST['instagram_widget_id'] ) ? (int) $_POST['instagram_widget_id'] : null;
|
||||
|
||||
// For Simple sites check if the token is valid.
|
||||
// (For Atomic sites, this check is done via the api: wpcom/v2/instagram/<token_id>).
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$token = Keyring::init()->get_token_store()->get_token(
|
||||
array(
|
||||
'type' => 'access',
|
||||
'id' => $token_id,
|
||||
)
|
||||
);
|
||||
if ( get_current_user_id() !== (int) $token->meta['user_id'] ) {
|
||||
return wp_send_json_error( array( 'message' => 'not_authorized' ), 403 );
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_widget_token_id( $token_id, $widget_id );
|
||||
$this->update_widget_token_legacy_status( false );
|
||||
|
||||
return wp_send_json_success( null, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the widget's option in the database to show if it is for legacy API or not.
|
||||
*
|
||||
* @param bool $is_legacy_token A flag to indicate if a token is for the legacy Instagram API.
|
||||
*/
|
||||
public function update_widget_token_legacy_status( $is_legacy_token ) {
|
||||
$widget_options = $this->get_settings();
|
||||
|
||||
if ( ! is_array( $widget_options[ $this->number ] ) ) {
|
||||
$widget_options[ $this->number ] = $this->defaults;
|
||||
}
|
||||
|
||||
$widget_options[ $this->number ]['is_legacy_token'] = $is_legacy_token;
|
||||
$this->save_settings( $widget_options );
|
||||
|
||||
return $is_legacy_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the status of the token from the API
|
||||
*
|
||||
* @param int $token_id A Keyring token ID.
|
||||
* @return array The status of the token's connection.
|
||||
*/
|
||||
private function get_token_status( $token_id ) {
|
||||
if ( empty( $token_id ) ) {
|
||||
return array( 'valid' => false );
|
||||
}
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$token = Keyring::init()->get_token_store()->get_token(
|
||||
array(
|
||||
'type' => 'access',
|
||||
'id' => $token_id,
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
'valid' => ! empty( $token ),
|
||||
'legacy' => $token && 'instagram' === $token->name,
|
||||
);
|
||||
}
|
||||
|
||||
$site = Jetpack_Options::get_option( 'id' );
|
||||
$path = sprintf( '/sites/%s/instagram/%s/check-token', $site, $token_id );
|
||||
$result = Client::wpcom_json_api_request_as_blog( $path, 2, array( 'headers' => array( 'content-type' => 'application/json' ) ), null, 'wpcom' );
|
||||
$response_code = wp_remote_retrieve_response_code( $result );
|
||||
if ( 200 !== $response_code ) {
|
||||
return array(
|
||||
// We assume the token is valid if the response_code is anything but the invalid
|
||||
// token codes we send back. This is to make sure it's not reset, if the API is down
|
||||
// or something.
|
||||
'valid' => ! ( 403 === $response_code || 401 === $response_code ),
|
||||
'legacy' => 'ERROR',
|
||||
);
|
||||
}
|
||||
$status = json_decode( $result['body'], true );
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the widget instance's token ID and then uses it to fetch images from Instagram.
|
||||
* It then caches the result which it will use on subsequent pageviews.
|
||||
* Keyring is not loaded nor is a remote request is not made in the event of a cache hit.
|
||||
*
|
||||
* @param array $instance A widget $instance, as passed to a widget's widget() method.
|
||||
* @return WP_Error|array A WP_Error on error, an array of images on success.
|
||||
*/
|
||||
public function get_data( $instance ) {
|
||||
if ( empty( $instance['token_id'] ) ) {
|
||||
return new WP_Error( 'empty_token', esc_html__( 'The token id was empty', 'jetpack' ), 403 );
|
||||
}
|
||||
|
||||
$transient_key = implode( '|', array( 'jetpack_instagram_widget', $instance['token_id'], $instance['count'] ) );
|
||||
$cached_images = get_transient( $transient_key );
|
||||
if ( $cached_images ) {
|
||||
return $cached_images;
|
||||
}
|
||||
|
||||
$site = Jetpack_Options::get_option( 'id' );
|
||||
$path = sprintf( '/sites/%s/instagram/%s?count=%s', $site, $instance['token_id'], $instance['count'] );
|
||||
$result = Client::wpcom_json_api_request_as_blog( $path, 2, array( 'headers' => array( 'content-type' => 'application/json' ) ), null, 'wpcom' );
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $result );
|
||||
if ( 200 !== $response_code ) {
|
||||
return new WP_Error( 'invalid_response', esc_html__( 'The response was invalid', 'jetpack' ), $response_code );
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $result ), true );
|
||||
if ( ! isset( $data['images'] ) || ! is_array( $data['images'] ) ) {
|
||||
return new WP_Error( 'missing_images', esc_html__( 'The images were missing', 'jetpack' ), $response_code );
|
||||
}
|
||||
|
||||
set_transient( $transient_key, $data, HOUR_IN_SECONDS );
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the contents of the widget on the front end.
|
||||
*
|
||||
* If the widget is unconfigured, a configuration message is displayed to users with admin access
|
||||
* and the entire widget is hidden from everyone else to avoid displaying an empty widget.
|
||||
*
|
||||
* @param array $args The sidebar arguments that control the wrapping HTML.
|
||||
* @param array $instance The widget instance (configuration options).
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
$data = $this->get_data( $instance );
|
||||
if ( is_wp_error( $data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$images = $data['images'];
|
||||
|
||||
$status = $this->get_token_status( $instance['token_id'] );
|
||||
// Don't display anything to non-blog admins if the widgets is unconfigured or API call fails.
|
||||
if ( ( ! $status['valid'] || ! is_array( $images ) ) && ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_css();
|
||||
|
||||
echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
// Always show a title on an unconfigured widget.
|
||||
if ( ! $status['valid'] && empty( $instance['title'] ) ) {
|
||||
$instance['title'] = $this->defaults['title'];
|
||||
}
|
||||
|
||||
if ( ! empty( $instance['title'] ) ) {
|
||||
echo $args['before_title']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $instance['title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['after_title']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( $status['valid'] && current_user_can( 'edit_theme_options' ) && $status['legacy'] ) {
|
||||
echo '<p><em>' . sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s is a link to reconnect the Instagram widget */
|
||||
__( 'In order to continue using this widget you must <a href="%s">reconnect to Instagram</a>.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( add_query_arg( 'instagram_widget_id', $this->number, admin_url( 'widgets.php' ) ) )
|
||||
) . '</em></p>';
|
||||
}
|
||||
|
||||
if ( ! $status['valid'] ) {
|
||||
echo '<p><em>' . sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s is a link to configure the Instagram widget */
|
||||
__( 'In order to use this Instagram widget, you must <a href="%s">configure it</a> first.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( add_query_arg( 'instagram_widget_id', $this->number, admin_url( 'widgets.php' ) ) )
|
||||
) . '</em></p>';
|
||||
} elseif ( ! is_array( $images ) ) {
|
||||
echo '<p>' . esc_html__( 'There was an error retrieving images from Instagram. An attempt will be remade in a few minutes.', 'jetpack' ) . '</p>';
|
||||
} elseif ( ! $images ) {
|
||||
echo '<p>' . esc_html__( 'No Instagram images were found.', 'jetpack' ) . '</p>';
|
||||
} else {
|
||||
echo '<div class="' . esc_attr( 'wpcom-instagram-images wpcom-instagram-columns-' . (int) $instance['columns'] ) . '">' . "\n";
|
||||
foreach ( $images as $image ) {
|
||||
/**
|
||||
* Filter how Instagram image links open in the Instagram widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 8.8.0
|
||||
*
|
||||
* @param string $target Target attribute.
|
||||
*/
|
||||
$image_target = apply_filters( 'wpcom_instagram_widget_target', '_self' );
|
||||
echo '<a href="' . esc_url( $image['link'] ) . '" target="' . esc_attr( $image_target ) . '"><div class="sq-bg-image" style="background-image: url(' . esc_url( set_url_scheme( $image['url'] ) ) . ')"><span class="screen-reader-text">' . esc_attr( $image['title'] ) . '</span></div></a>' . "\n";
|
||||
}
|
||||
echo "</div>\n";
|
||||
}
|
||||
|
||||
echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'instagram' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to connect the widget to Instagram
|
||||
*
|
||||
* @return string the conneciton URL.
|
||||
*/
|
||||
private function get_connect_url() {
|
||||
$connect_url = '';
|
||||
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM && function_exists( 'wpcom_keyring_get_connect_URL' ) ) {
|
||||
$connect_url = wpcom_keyring_get_connect_URL( 'instagram-basic-display', 'instagram-widget' );
|
||||
} else {
|
||||
$jetpack_blog_id = Jetpack_Options::get_option( 'id' );
|
||||
$response = Client::wpcom_json_api_request_as_user(
|
||||
sprintf( '/sites/%d/external-services', $jetpack_blog_id )
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$body = json_decode( $response['body'] );
|
||||
$connect_url = new WP_Error( 'connect_url_not_found', 'Connect URL not found' );
|
||||
if ( ! empty( $body->services->{'instagram-basic-display'}->connect_URL ) ) {
|
||||
$connect_url = $body->services->{'instagram-basic-display'}->connect_URL;
|
||||
}
|
||||
}
|
||||
|
||||
return $connect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request trying to remove the widgets stored id?
|
||||
*
|
||||
* @param array $status The status of the token's connection.
|
||||
* @return bool if this request trying to remove the widgets stored id.
|
||||
*/
|
||||
public function removing_widgets_stored_id( $status ) {
|
||||
return $status['valid'] && isset( $_GET['instagram_widget_id'] ) && (int) $_GET['instagram_widget_id'] === (int) $this->number && ! empty( $_GET['instagram_widget'] ) && 'remove_token' === $_GET['instagram_widget']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget configuration form for the widget administration page.
|
||||
* Allows the user to add new Instagram Keyring tokens and more.
|
||||
*
|
||||
* @param array $instance The widget instance (configuration options).
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
|
||||
if ( ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) && ! ( new Manager() )->is_user_connected() ) {
|
||||
echo '<p>';
|
||||
printf(
|
||||
// translators: %1$1 and %2$s are the opening and closing a tags creating a link to the Jetpack dashboard.
|
||||
esc_html__( 'In order to use this widget you need to %1$scomplete your Jetpack connection%2$s by authorizing your user.', 'jetpack' ),
|
||||
'<a href="' . esc_url( Jetpack::admin_url( array( 'page' => 'jetpack#/connect-user' ) ) ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
echo '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// If coming back to the widgets page from an action, expand this widget.
|
||||
if ( isset( $_GET['instagram_widget_id'] ) && (int) $_GET['instagram_widget_id'] === (int) $this->number ) {
|
||||
echo '<script type="text/javascript">jQuery(document).ready(function($){ $(\'.widget[id$="wpcom_instagram_widget-' . esc_js( $this->number ) . '"] .widget-inside\').slideDown(\'fast\'); });</script>';
|
||||
}
|
||||
|
||||
$status = $this->get_token_status( $instance['token_id'] );
|
||||
|
||||
// If removing the widget's stored token ID.
|
||||
if ( $this->removing_widgets_stored_id( $status ) ) {
|
||||
if ( empty( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), 'instagram-widget-remove-token-' . $this->number . '-' . $instance['token_id'] ) ) {
|
||||
wp_die( esc_html__( 'Missing or invalid security nonce.', 'jetpack' ) );
|
||||
}
|
||||
|
||||
$instance['token_id'] = $this->defaults['token_id'];
|
||||
|
||||
$this->update_widget_token_id( $instance['token_id'] );
|
||||
$this->update_widget_token_legacy_status( false );
|
||||
} elseif ( $status['valid'] && ( ! isset( $instance['is_legacy_token'] ) || 'ERROR' === $instance['is_legacy_token'] ) ) { // If a token ID is stored, check if we know if it is a legacy API token or not.
|
||||
$instance['is_legacy_token'] = $this->update_widget_token_legacy_status( $status['legacy'] );
|
||||
} elseif ( ! $status['valid'] ) { // If the token isn't valid reset it.
|
||||
$instance['token_id'] = $this->defaults['token_id'];
|
||||
$this->update_widget_token_id( $instance['token_id'] );
|
||||
}
|
||||
|
||||
// No connection, or a legacy API token? Display a connection link.
|
||||
$is_legacy_token = ( isset( $instance['is_legacy_token'] ) && true === $instance['is_legacy_token'] );
|
||||
|
||||
if ( $is_legacy_token ) {
|
||||
echo '<p><strong>' . esc_html__( 'In order to continue using this widget you must reconnect to Instagram.', 'jetpack' ) . '</strong></p>';
|
||||
}
|
||||
|
||||
if ( is_customize_preview() && ! $instance['token_id'] ) {
|
||||
echo '<p>';
|
||||
echo wp_kses(
|
||||
__( '<strong>Important: You must first click Publish to activate this widget <em>before</em> connecting your account.</strong> After saving the widget, click the button below to connect your Instagram account.', 'jetpack' ),
|
||||
array(
|
||||
'strong' => array(),
|
||||
'em' => array(),
|
||||
)
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
if ( ! $instance['token_id'] || $is_legacy_token ) {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
function getScreenCenterSpecs( width, height ) {
|
||||
const screenTop = typeof window.screenTop !== 'undefined' ? window.screenTop : window.screenY,
|
||||
screenLeft = typeof window.screenLeft !== 'undefined' ? window.screenLeft : window.screenX;
|
||||
|
||||
return [
|
||||
'width=' + width,
|
||||
'height=' + height,
|
||||
'top=' + ( screenTop + window.innerHeight / 2 - height / 2 ),
|
||||
'left=' + ( screenLeft + window.innerWidth / 2 - width / 2 ),
|
||||
].join();
|
||||
};
|
||||
function openWindow( button ) {
|
||||
// let's just double check that we aren't getting an unknown random domain injected in here somehow.
|
||||
if (! /^https:\/\/public-api.wordpress.com\/connect\//.test(button.dataset.connecturl) ) {
|
||||
return;
|
||||
}
|
||||
window.open(
|
||||
button.dataset.connecturl, //TODO: Check if this needs validation it could be a XSS problem. Check the domain maybe?
|
||||
'_blank',
|
||||
'toolbar=0,location=0,menubar=0,' + getScreenCenterSpecs( 700, 700 )
|
||||
);
|
||||
button.innerText = '<?php echo esc_js( __( 'Connecting…', 'jetpack' ) ); ?>';
|
||||
button.disabled = true;
|
||||
window.onmessage = function( { data } ) {
|
||||
if ( !! data.keyring_id ) {
|
||||
var payload = {
|
||||
action: 'wpcom_instagram_widget_update_widget_token_id',
|
||||
savetoken: '<?php echo esc_js( wp_create_nonce( 'instagram-widget-save-token' ) ); ?>',
|
||||
keyring_id: data.keyring_id,
|
||||
instagram_widget_id: button.dataset.widgetid,
|
||||
};
|
||||
jQuery.post( ajaxurl, payload, function( response ) {
|
||||
var widget = jQuery(button).closest('div.widget');
|
||||
if ( ! window.wpWidgets ) {
|
||||
window.location = '<?php echo esc_js( add_query_arg( array( 'autofocus[panel]' => 'widgets' ), admin_url( 'customize.php' ) ) ); ?>';
|
||||
} else {
|
||||
wpWidgets.save( widget, 0, 1, 1 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
$connect_url = $this->get_connect_url();
|
||||
if ( is_wp_error( $connect_url ) ) {
|
||||
echo '<p>' . esc_html__( 'Instagram is currently experiencing connectivity issues, please try again later to connect.', 'jetpack' ) . '</p>';
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<p style="text-align:center"><button class="button-primary" onclick="openWindow(this); return false;" data-widgetid="<?php echo esc_attr( $this->number ); ?>" data-connecturl="<?php echo esc_attr( $connect_url ); ?>"><?php echo esc_html( __( 'Connect Instagram Account', 'jetpack' ) ); ?></button></p>
|
||||
|
||||
<?php // Include hidden fields for the widget settings before a connection is made, otherwise the default settings are lost after connecting. ?>
|
||||
<input type="hidden" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
<input type="hidden" id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>" value="<?php echo esc_attr( $instance['count'] ); ?>" />
|
||||
<input type="hidden" id="<?php echo esc_attr( $this->get_field_id( 'columns' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'columns' ) ); ?>" value="<?php echo esc_attr( $instance['columns'] ); ?>" />
|
||||
|
||||
<?php
|
||||
echo '<p><small>' . sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s is a link to log in to Instagram */
|
||||
__( 'Having trouble? Try <a href="%s" target="_blank" rel="noopener noreferrer">logging into the correct account</a> on Instagram.com first.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
'rel' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
'https://instagram.com/accounts/login/'
|
||||
) . '</small></p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Connected account.
|
||||
$page = ( is_customize_preview() ) ? 'customize.php' : 'widgets.php';
|
||||
|
||||
$query_args = array(
|
||||
'instagram_widget_id' => $this->number,
|
||||
'instagram_widget' => 'remove_token',
|
||||
'nonce' => wp_create_nonce( 'instagram-widget-remove-token-' . $this->number . '-' . $instance['token_id'] ),
|
||||
);
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
$query_args['autofocus[panel]'] = 'widgets';
|
||||
}
|
||||
|
||||
$remove_token_id_url = add_query_arg( $query_args, admin_url( $page ) );
|
||||
|
||||
$data = $this->get_data( $instance );
|
||||
// TODO: Revisit the error handling. I think we should be using WP_Error here and
|
||||
// Jetpack::Client is the legacy check.
|
||||
if ( is_wp_error( $data ) || 'ERROR' === $instance['is_legacy_token'] ) {
|
||||
echo '<p>' . esc_html__( 'Instagram is currently experiencing connectivity issues, please try again later to connect.', 'jetpack' ) . '</p>';
|
||||
return;
|
||||
}
|
||||
echo '<p>';
|
||||
printf(
|
||||
wp_kses(
|
||||
/* translators: %1$s is the URL of the connected Instagram account, %2$s is the username of the connected Instagram account, %3$s is the URL to disconnect the account. */
|
||||
__( '<strong>Connected Instagram Account</strong><br /> <a target="_blank" rel="noopener noreferrer" href="%1$s">%2$s</a> | <a href="%3$s">remove</a>', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'rel' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
'strong' => array(),
|
||||
'br' => array(),
|
||||
)
|
||||
),
|
||||
esc_url( 'https://instagram.com/' . $data['external_name'] ),
|
||||
esc_html( $data['external_name'] ),
|
||||
esc_url( $remove_token_id_url )
|
||||
);
|
||||
echo '</p>';
|
||||
|
||||
// Title.
|
||||
echo '<p><label><strong>' . esc_html__( 'Widget Title', 'jetpack' ) . '</strong> <input type="text" id="' . esc_attr( $this->get_field_id( 'title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'title' ) ) . '" value="' . esc_attr( $instance['title'] ) . '" class="widefat" /></label></p>';
|
||||
|
||||
// Number of images to show.
|
||||
echo '<p><label>';
|
||||
echo '<strong>' . esc_html__( 'Images', 'jetpack' ) . '</strong><br />';
|
||||
echo esc_html__( 'Number to display:', 'jetpack' ) . ' ';
|
||||
echo '<select name="' . esc_attr( $this->get_field_name( 'count' ) ) . '">';
|
||||
for ( $i = 1; $i <= $this->valid_options['max_count']; $i++ ) {
|
||||
echo '<option value="' . esc_attr( $i ) . '"' . selected( $i, $instance['count'], false ) . '>' . esc_attr( $i ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</label></p>';
|
||||
|
||||
// Columns.
|
||||
echo '<p><label>';
|
||||
echo '<strong>' . esc_html__( 'Layout', 'jetpack' ) . '</strong><br />';
|
||||
echo esc_html__( 'Number of columns:', 'jetpack' ) . ' ';
|
||||
echo '<select name="' . esc_attr( $this->get_field_name( 'columns' ) ) . '">';
|
||||
for ( $i = 1; $i <= $this->valid_options['max_columns']; $i++ ) {
|
||||
echo '<option value="' . esc_attr( $i ) . '"' . selected( $i, $instance['columns'], false ) . '>' . esc_attr( $i ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</label></p>';
|
||||
|
||||
echo '<p><small>' . esc_html__( 'New images may take up to 15 minutes to show up on your site.', 'jetpack' ) . '</small></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sanitizes the user-supplied widget options.
|
||||
*
|
||||
* @param array $new_instance The user-supplied values.
|
||||
* @param array $old_instance The existing widget options.
|
||||
* @return array A validated and sanitized version of $new_instance.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = $this->defaults;
|
||||
|
||||
if ( ! empty( $old_instance['token_id'] ) ) {
|
||||
$instance['token_id'] = $old_instance['token_id'];
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['title'] ) ) {
|
||||
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['columns'] ) ) {
|
||||
$instance['columns'] = max( 1, min( $this->valid_options['max_columns'], (int) $new_instance['columns'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['count'] ) ) {
|
||||
$instance['count'] = max( 1, min( $this->valid_options['max_count'], (int) $new_instance['count'] ) );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
add_action(
|
||||
'widgets_init',
|
||||
function () {
|
||||
if ( Jetpack::is_connection_ready() ) {
|
||||
register_widget( 'Jetpack_Instagram_Widget' );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Redirect;
|
||||
|
||||
if ( ! class_exists( 'Jetpack_Contact_Info_Widget' ) ) {
|
||||
|
||||
/**
|
||||
* Register Contact_Info_Widget widget
|
||||
*/
|
||||
function jetpack_contact_info_widget_init() {
|
||||
register_widget( 'Jetpack_Contact_Info_Widget' );
|
||||
}
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_contact_info_widget_init' );
|
||||
|
||||
/**
|
||||
* Makes a custom Widget for displaying Restaurant Location/Map, Hours, and Contact Info available.
|
||||
*
|
||||
* @package WordPress
|
||||
*/
|
||||
class Jetpack_Contact_Info_Widget extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
global $pagenow;
|
||||
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget_contact_info',
|
||||
'description' => __( 'Display a map with your location, hours, and contact information.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct(
|
||||
'widget_contact_info',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Contact Info & Map', 'jetpack' ) ),
|
||||
$widget_ops
|
||||
);
|
||||
$this->alt_option_name = 'widget_contact_info';
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
} elseif ( 'widgets.php' === $pagenow ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_customize-contact-info-api-key', array( $this, 'ajax_check_api_key' ) );
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Contact info and Map" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'widget_contact_info';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_style(
|
||||
'contact-info-map-css',
|
||||
plugins_url( 'contact-info/contact-info-map.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Array of default values for the Widget's options
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'title' => __( 'Hours & Info', 'jetpack' ),
|
||||
'address' => __( "3999 Mission Boulevard,\nSan Diego CA 92109", 'jetpack' ),
|
||||
'phone' => _x( '1-202-555-1212', 'Example of a phone number', 'jetpack' ),
|
||||
'hours' => __( "Lunch: 11am - 2pm \nDinner: M-Th 5pm - 11pm, Fri-Sat:5pm - 1am", 'jetpack' ),
|
||||
'email' => null,
|
||||
'showmap' => 0,
|
||||
'apikey' => null,
|
||||
'goodmap' => null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the HTML for this widget.
|
||||
*
|
||||
* @param array $args An array of standard parameters for widgets in this theme.
|
||||
* @param array $instance An array of settings for this widget instance.
|
||||
*
|
||||
* @return void Echoes it's output
|
||||
**/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( '' !== $instance['title'] ) {
|
||||
echo $args['before_title'] . $instance['title'] . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires at the beginning of the Contact Info widget, after the title.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.2
|
||||
*/
|
||||
do_action( 'jetpack_contact_info_widget_start' );
|
||||
|
||||
echo '<div itemscope itemtype="http://schema.org/LocalBusiness">';
|
||||
|
||||
if ( '' !== $instance['address'] ) {
|
||||
|
||||
$showmap = $instance['showmap'];
|
||||
$goodmap = isset( $instance['goodmap'] ) ? $instance['goodmap'] : $this->has_good_map( $instance );
|
||||
|
||||
if ( $showmap && true === $goodmap ) {
|
||||
/**
|
||||
* Set a Google Maps API Key.
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param string $api_key Google Maps API Key
|
||||
*/
|
||||
$api_key = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
|
||||
echo $this->build_map( $instance['address'], $api_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} elseif ( $showmap && is_customize_preview() && true !== $goodmap ) {
|
||||
printf(
|
||||
'<span class="contact-map-api-error" style="display: block;">%s</span>',
|
||||
esc_html( $instance['goodmap'] )
|
||||
);
|
||||
}
|
||||
|
||||
$map_link = $this->build_map_link( $instance['address'] );
|
||||
|
||||
printf(
|
||||
'<div class="confit-address" itemscope itemtype="http://schema.org/PostalAddress" itemprop="address"><a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a></div>',
|
||||
esc_url( $map_link ),
|
||||
str_replace( "\n", '<br/>', esc_html( $instance['address'] ) ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
if ( '' !== $instance['phone'] ) {
|
||||
if ( wp_is_mobile() ) {
|
||||
echo '<div class="confit-phone"><span itemprop="telephone"><a href="' . esc_url( 'tel:' . $instance['phone'] ) . '">' . esc_html( $instance['phone'] ) . '</a></span></div>';
|
||||
} else {
|
||||
echo '<div class="confit-phone"><span itemprop="telephone">' . esc_html( $instance['phone'] ) . '</span></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_email( trim( $instance['email'] ) ) ) {
|
||||
printf(
|
||||
'<div class="confit-email"><a href="mailto:%1$s">%1$s</a></div>',
|
||||
esc_html( $instance['email'] )
|
||||
);
|
||||
}
|
||||
|
||||
if ( '' !== $instance['hours'] ) {
|
||||
printf(
|
||||
'<div class="confit-hours" itemprop="openingHours">%s</div>',
|
||||
str_replace( "\n", '<br/>', esc_html( $instance['hours'] ) ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
|
||||
/**
|
||||
* Fires at the end of Contact Info widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.2
|
||||
*/
|
||||
do_action( 'jetpack_contact_info_widget_end' );
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'contact_info' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with the settings when they are saved by the admin. Here is
|
||||
* where any validation should be dealt with.
|
||||
*
|
||||
* @param array $new_instance New configuration values.
|
||||
* @param array $old_instance Old configuration values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
|
||||
$instance = array();
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
$instance['address'] = wp_kses( $new_instance['address'], array() );
|
||||
$instance['phone'] = wp_kses( $new_instance['phone'], array() );
|
||||
$instance['email'] = wp_kses( $new_instance['email'], array() );
|
||||
$instance['hours'] = wp_kses( $new_instance['hours'], array() );
|
||||
$instance['apikey'] = wp_kses( isset( $new_instance['apikey'] ) ? $new_instance['apikey'] : $old_instance['apikey'], array() );
|
||||
|
||||
if ( ! isset( $new_instance['showmap'] ) ) {
|
||||
$instance['showmap'] = 0;
|
||||
} else {
|
||||
$instance['showmap'] = (int) $new_instance['showmap'];
|
||||
}
|
||||
|
||||
$instance['goodmap'] = $this->update_goodmap( $old_instance, $instance );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the form for this widget on the Widgets page of the WP Admin area.
|
||||
*
|
||||
* @param array $instance Instance configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
/** This filter is documented in modules/widgets/contact-info.php */
|
||||
$apikey = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
|
||||
|
||||
wp_enqueue_script(
|
||||
'contact-info-admin',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/contact-info/contact-info-admin.min.js',
|
||||
'modules/widgets/contact-info/contact-info-admin.js'
|
||||
),
|
||||
array( 'jquery' ),
|
||||
20160727,
|
||||
false
|
||||
);
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
$customize_contact_info_api_key_nonce = wp_create_nonce( 'customize_contact_info_api_key' );
|
||||
wp_localize_script(
|
||||
'contact-info-admin',
|
||||
'contact_info_api_key_ajax_obj',
|
||||
array( 'nonce' => $customize_contact_info_api_key_nonce )
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'address' ) ); ?>"><?php esc_html_e( 'Address:', 'jetpack' ); ?></label>
|
||||
<textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'address' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'address' ) ); ?>"><?php echo esc_textarea( $instance['address'] ); ?></textarea>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input class="jp-contact-info-showmap" id="<?php echo esc_attr( $this->get_field_id( 'showmap' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showmap' ) ); ?>" value="1" type="checkbox" <?php checked( $instance['showmap'], 1 ); ?> />
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'showmap' ) ); ?>"><?php esc_html_e( 'Show map', 'jetpack' ); ?></label>
|
||||
</p>
|
||||
|
||||
<?php if ( ! has_filter( 'jetpack_google_maps_api_key' ) || false === apply_filters( 'jetpack_google_maps_api_key', false ) ) { ?>
|
||||
|
||||
<p class="jp-contact-info-admin-map" style="<?php echo $instance['showmap'] ? '' : 'display: none;'; ?>">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'apikey' ) ); ?>">
|
||||
<?php esc_html_e( 'Google Maps API Key', 'jetpack' ); ?>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'apikey' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'apikey' ) ); ?>" type="text" value="<?php echo esc_attr( $apikey ); ?>" />
|
||||
<br />
|
||||
<small>
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* Translators: placeholder is a URL to support documentation. */
|
||||
__( 'Google now requires an API key to use their maps on your site. <a href="%s">See our documentation</a> for instructions on acquiring a key.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
),
|
||||
)
|
||||
),
|
||||
( defined( 'IS_WPCOM' ) && IS_WPCOM ) ? 'https://wordpress.com/support/widgets/contact-info/' : esc_url( Redirect::get_url( 'jetpack-support-extra-sidebar-widgets-contact-info-widget' ) )
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<input type="hidden" id="<?php echo esc_attr( $this->get_field_id( 'apikey' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'apikey' ) ); ?>" value="<?php echo esc_attr( $apikey ); ?>" />
|
||||
|
||||
<?php } // end if jetpack_google_maps_api_key check. ?>
|
||||
|
||||
<p class="jp-contact-info-admin-map jp-contact-info-embed-map" style="<?php echo $instance['showmap'] ? '' : 'display: none;'; ?>">
|
||||
<?php
|
||||
if ( ! is_customize_preview() && true === $instance['goodmap'] ) {
|
||||
echo $this->build_map( $instance['address'], $apikey ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} elseif ( true !== $instance['goodmap'] && ! empty( $instance['goodmap'] ) ) {
|
||||
printf(
|
||||
'<span class="button-link-delete">%s</span>',
|
||||
esc_html( $instance['goodmap'] )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>"><?php esc_html_e( 'Phone:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'phone' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['phone'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>"><?php esc_html_e( 'Email Address:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['email'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'hours' ) ); ?>"><?php esc_html_e( 'Hours:', 'jetpack' ); ?></label>
|
||||
<textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'hours' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'hours' ) ); ?>"><?php echo esc_textarea( $instance['hours'] ); ?></textarea>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Google Maps link for the supplied address.
|
||||
*
|
||||
* @param string $address Address to link to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function build_map_link( $address ) {
|
||||
// Google map urls have lots of available params but zoom (z) and query (q) are enough.
|
||||
return 'https://maps.google.com/maps?z=16&q=' . $this->urlencode_address( $address );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds map display HTML code from the supplied address.
|
||||
*
|
||||
* @param string $address Address.
|
||||
* @param string $api_key API Key.
|
||||
*
|
||||
* @return string HTML of the map.
|
||||
*/
|
||||
private function build_map( $address, $api_key = null ) {
|
||||
$this->enqueue_scripts();
|
||||
$src = add_query_arg( 'q', rawurlencode( $address ), 'https://www.google.com/maps/embed/v1/place' );
|
||||
if ( ! empty( $api_key ) ) {
|
||||
$src = add_query_arg( 'key', $api_key, $src );
|
||||
}
|
||||
|
||||
$height = 216;
|
||||
|
||||
$iframe_attributes = sprintf(
|
||||
' height="%d" frameborder="0" src="%s" title="%s" class="contact-map"',
|
||||
esc_attr( $height ),
|
||||
esc_url( $src ),
|
||||
__( 'Google Map Embed', 'jetpack' )
|
||||
);
|
||||
|
||||
$iframe_html = sprintf( '<iframe width="600" %s></iframe>', $iframe_attributes );
|
||||
|
||||
if (
|
||||
! class_exists( 'Jetpack_AMP_Support' )
|
||||
|| ! Jetpack_AMP_Support::is_amp_request()
|
||||
) {
|
||||
return $iframe_html;
|
||||
}
|
||||
|
||||
$amp_iframe_html = sprintf( '<amp-iframe layout="fixed-height" width="auto" sandbox="allow-scripts allow-same-origin" %s>', $iframe_attributes );
|
||||
|
||||
// Add placeholder to avoid AMP error: <amp-iframe> elements must be positioned outside the first 75% of the viewport or 600px from the top (whichever is smaller).
|
||||
$amp_iframe_html .= sprintf( '<span placeholder>%s</span>', esc_html__( 'Loading map…', 'jetpack' ) );
|
||||
|
||||
// Add original iframe as fallback in case JavaScript is disabled.
|
||||
$amp_iframe_html .= sprintf( '<noscript>%s</noscript>', $iframe_html );
|
||||
|
||||
$amp_iframe_html .= '</amp-iframe>';
|
||||
return $amp_iframe_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an URL
|
||||
*
|
||||
* @param string $address The URL to encode.
|
||||
*
|
||||
* @return string The encoded URL
|
||||
*/
|
||||
private function urlencode_address( $address ) {
|
||||
|
||||
$address = strtolower( $address );
|
||||
// Get rid of any unwanted whitespace.
|
||||
$address = preg_replace( '/\s+/', ' ', trim( $address ) );
|
||||
// Use + not %20.
|
||||
$address = str_ireplace( ' ', '+', $address );
|
||||
return rawurlencode( $address );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance's updated 'goodmap' value.
|
||||
*
|
||||
* @param array $old_instance Old configuration values.
|
||||
* @param array $instance Current configuration values.
|
||||
*
|
||||
* @return bool|string The instance's updated 'goodmap' value. The value is true if
|
||||
* $instance can display a good map. If not, returns an error message.
|
||||
*/
|
||||
private function update_goodmap( $old_instance, $instance ) {
|
||||
/*
|
||||
* If we have no address or don't want to show a map,
|
||||
* no need to check if the map is valid.
|
||||
*/
|
||||
if ( empty( $instance['address'] ) || 0 === $instance['showmap'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there have been any changes that may impact the map in the widget
|
||||
* (adding an address, address changes, new API key, API key change)
|
||||
* then we want to check whether our map can be displayed again.
|
||||
*/
|
||||
if (
|
||||
! isset( $instance['goodmap'] )
|
||||
|| ! isset( $old_instance['address'] )
|
||||
|| $this->urlencode_address( $old_instance['address'] ) !== $this->urlencode_address( $instance['address'] )
|
||||
|| ! isset( $old_instance['apikey'] )
|
||||
|| $old_instance['apikey'] !== $instance['apikey']
|
||||
) {
|
||||
return $this->has_good_map( $instance );
|
||||
} else {
|
||||
return $instance['goodmap'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the instance has a valid Map location.
|
||||
*
|
||||
* @param array $instance Widget instance configuration.
|
||||
*
|
||||
* @return bool|string Whether or not there is a valid map. If not, return an error message.
|
||||
*/
|
||||
private function has_good_map( $instance ) {
|
||||
/** This filter is documented in modules/widgets/contact-info.php */
|
||||
$api_key = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
|
||||
if ( ! empty( $api_key ) ) {
|
||||
$path = add_query_arg(
|
||||
array(
|
||||
'q' => rawurlencode( $instance['address'] ),
|
||||
'key' => $api_key,
|
||||
),
|
||||
'https://www.google.com/maps/embed/v1/place'
|
||||
);
|
||||
$wp_remote_get_args = array(
|
||||
'headers' => array( 'Referer' => home_url() ),
|
||||
);
|
||||
$response = wp_remote_get( esc_url_raw( $path ), $wp_remote_get_args );
|
||||
|
||||
if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
|
||||
return true;
|
||||
} else {
|
||||
return wp_remote_retrieve_body( $response );
|
||||
}
|
||||
}
|
||||
|
||||
return __( 'Please enter a valid Google API Key.', 'jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the Google Maps API key after an Ajax call from the widget's admin form in
|
||||
* the Customizer preview.
|
||||
*/
|
||||
public function ajax_check_api_key() {
|
||||
if ( isset( $_POST['apikey'] ) ) {
|
||||
if ( check_ajax_referer( 'customize_contact_info_api_key' ) && current_user_can( 'customize' ) ) {
|
||||
$apikey = wp_kses( wp_unslash( $_POST['apikey'] ), array() );
|
||||
$default_instance = $this->defaults();
|
||||
$default_instance['apikey'] = $apikey;
|
||||
wp_send_json( array( 'result' => esc_html( $this->has_good_map( $default_instance ) ) ) );
|
||||
}
|
||||
} else {
|
||||
wp_die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* global ajaxurl, contact_info_api_key_ajax_obj */
|
||||
|
||||
( function ( $ ) {
|
||||
$( document ).on( 'change', '.jp-contact-info-showmap', function () {
|
||||
var $checkbox = $( this ),
|
||||
isChecked = $checkbox.is( ':checked' );
|
||||
|
||||
$checkbox.closest( '.widget' ).find( '.jp-contact-info-admin-map' ).toggle( isChecked );
|
||||
} );
|
||||
|
||||
$( document ).on( 'widget-synced', function ( event, widgetContainer ) {
|
||||
// This event fires for all widgets, so restrict this to Contact Info widgets and the API key input.
|
||||
if (
|
||||
! widgetContainer.is( '[id*="widget_contact_info"]' ) ||
|
||||
! $( document.activeElement ).is( 'input[id*="apikey"]' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var $apikey_input = widgetContainer.find( 'input[id*="apikey"]' );
|
||||
|
||||
$.post(
|
||||
ajaxurl,
|
||||
{
|
||||
_ajax_nonce: contact_info_api_key_ajax_obj.nonce,
|
||||
action: 'customize-contact-info-api-key',
|
||||
apikey: $apikey_input.val(),
|
||||
},
|
||||
function ( data ) {
|
||||
var $map_element = $apikey_input
|
||||
.closest( '.jp-contact-info-admin-map' )
|
||||
.parent()
|
||||
.find( '.jp-contact-info-embed-map' );
|
||||
var $warning_span = $map_element.find( '[class*="notice"]' );
|
||||
|
||||
if ( '1' !== data.result ) {
|
||||
if ( $warning_span.length === 0 ) {
|
||||
$map_element.append(
|
||||
'<span class="notice notice-warning" style="display: block;">' +
|
||||
data.result +
|
||||
'</span>'
|
||||
);
|
||||
} else if ( $warning_span.text() !== data.result ) {
|
||||
$warning_span.text( data.result );
|
||||
}
|
||||
} else {
|
||||
$map_element.empty();
|
||||
}
|
||||
}
|
||||
);
|
||||
} );
|
||||
} )( window.jQuery );
|
||||
@@ -0,0 +1,13 @@
|
||||
.contact-map {
|
||||
max-width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.contact-map-api-error {
|
||||
border-left-color: #dba617;
|
||||
border-left-style: solid;
|
||||
border-left-width: 4px;
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
||||
margin: 5px 0 15px;
|
||||
padding: 1px 12px;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Utilities to stylize widget in Customizer controls.
|
||||
*/
|
||||
|
||||
/* My Community */
|
||||
#available-widgets [class*="community"] .widget-title:before { content: "\f307"; }
|
||||
@@ -0,0 +1,119 @@
|
||||
/* global gapi, FB, twttr, PaypalExpressCheckout */
|
||||
|
||||
/**
|
||||
* Utilities to work with widgets in Customizer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether this Customizer supports partial widget refresh.
|
||||
* @return {boolean}
|
||||
*/
|
||||
wp.customizerHasPartialWidgetRefresh = function () {
|
||||
return (
|
||||
'object' === typeof wp &&
|
||||
'function' === typeof wp.customize &&
|
||||
'object' === typeof wp.customize.selectiveRefresh &&
|
||||
'object' === typeof wp.customize.widgetsPreview &&
|
||||
'function' === typeof wp.customize.widgetsPreview.WidgetPartial
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the placed widget ID contains the widget name.
|
||||
* @param {object} placement
|
||||
* @param {string} widgetName
|
||||
* @return {*|boolean}
|
||||
*/
|
||||
wp.isJetpackWidgetPlaced = function ( placement, widgetName ) {
|
||||
return placement.partial.widgetId && 0 === placement.partial.widgetId.indexOf( widgetName );
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind events for selective refresh in Customizer.
|
||||
*/
|
||||
( function ( $ ) {
|
||||
$( document ).ready( function () {
|
||||
if ( wp && wp.customize && wp.customizerHasPartialWidgetRefresh() ) {
|
||||
// Refresh widget contents when a partial is rendered.
|
||||
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) {
|
||||
if ( placement.container ) {
|
||||
// Refresh Google+
|
||||
if (
|
||||
wp.isJetpackWidgetPlaced( placement, 'googleplus-badge' ) &&
|
||||
'object' === typeof gapi &&
|
||||
gapi.person &&
|
||||
'function' === typeof gapi.person.go
|
||||
) {
|
||||
gapi.person.go( placement.container[ 0 ] );
|
||||
}
|
||||
|
||||
// Refresh Facebook XFBML
|
||||
else if (
|
||||
wp.isJetpackWidgetPlaced( placement, 'facebook-likebox' ) &&
|
||||
'object' === typeof FB &&
|
||||
'object' === typeof FB.XFBML &&
|
||||
'function' === typeof FB.XFBML.parse
|
||||
) {
|
||||
FB.XFBML.parse( placement.container[ 0 ], function () {
|
||||
var $fbContainer = $( placement.container[ 0 ] ).find( '.fb_iframe_widget' ),
|
||||
fbWidth = $fbContainer.data( 'width' ),
|
||||
fbHeight = $fbContainer.data( 'height' );
|
||||
$fbContainer.find( 'span' ).css( { width: fbWidth, height: fbHeight } );
|
||||
setTimeout( function () {
|
||||
$fbContainer
|
||||
.find( 'iframe' )
|
||||
.css( { width: fbWidth, height: fbHeight, position: 'relative' } );
|
||||
}, 1 );
|
||||
} );
|
||||
}
|
||||
|
||||
// Refresh Twitter
|
||||
else if (
|
||||
wp.isJetpackWidgetPlaced( placement, 'twitter_timeline' ) &&
|
||||
'object' === typeof twttr &&
|
||||
'object' === typeof twttr.widgets &&
|
||||
'function' === typeof twttr.widgets.load
|
||||
) {
|
||||
twttr.widgets.load( placement.container[ 0 ] );
|
||||
} else if ( wp.isJetpackWidgetPlaced( placement, 'eu_cookie_law_widget' ) ) {
|
||||
// Refresh EU Cookie Law
|
||||
if ( $( '#eu-cookie-law' ).hasClass( 'top' ) ) {
|
||||
$( '.widget_eu_cookie_law_widget' ).addClass( 'top' );
|
||||
} else {
|
||||
$( '.widget_eu_cookie_law_widget' ).removeClass( 'top' );
|
||||
}
|
||||
placement.container.fadeIn();
|
||||
} else if ( wp.isJetpackWidgetPlaced( placement, 'jetpack_simple_payments_widget' ) ) {
|
||||
// Refresh Simple Payments Widget
|
||||
try {
|
||||
var buttonId = $( '.jetpack-simple-payments-button', placement.container )
|
||||
.attr( 'id' )
|
||||
.replace( '_button', '' );
|
||||
PaypalExpressCheckout.renderButton( null, null, buttonId, null );
|
||||
} catch {
|
||||
// PaypalExpressCheckout may fail.
|
||||
// For the same usage, see also:
|
||||
// https://github.com/Automattic/jetpack/blob/6c1971e6bed7d3df793392a7a58ffe0afaeeb5fe/modules/simple-payments/simple-payments.php#L111
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// Refresh widgets when they're moved.
|
||||
wp.customize.selectiveRefresh.bind( 'partial-content-moved', function ( placement ) {
|
||||
if ( placement.container ) {
|
||||
// Refresh Twitter timeline iframe, since it has to be re-built.
|
||||
if (
|
||||
wp.isJetpackWidgetPlaced( placement, 'twitter_timeline' ) &&
|
||||
placement.container.find( 'iframe.twitter-timeline:not([src]):first' ).length
|
||||
) {
|
||||
placement.partial.refresh();
|
||||
} else if ( wp.isJetpackWidgetPlaced( placement, 'jetpack_simple_payments_widget' ) ) {
|
||||
// Refresh Simple Payments Widget
|
||||
placement.partial.refresh();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,32 @@
|
||||
/* eslint no-var: 0 */
|
||||
|
||||
( function ( $ ) {
|
||||
var $document = $( document );
|
||||
|
||||
$document.ready( function () {
|
||||
var maybeShowNotice = function ( e, policyUrl ) {
|
||||
var $policyUrl = $( policyUrl || this ).closest( '.eu-cookie-law-widget-policy-url' );
|
||||
|
||||
if ( $policyUrl.find( 'input[type="radio"][value="default"]' ).is( ':checked' ) ) {
|
||||
$policyUrl.find( '.notice.default-policy' ).css( 'display', 'block' );
|
||||
$policyUrl.find( '.notice.custom-policy' ).hide();
|
||||
} else {
|
||||
$policyUrl.find( '.notice.default-policy' ).hide();
|
||||
$policyUrl.find( '.notice.custom-policy' ).css( 'display', 'block' );
|
||||
}
|
||||
};
|
||||
|
||||
$document.on(
|
||||
'click',
|
||||
'.eu-cookie-law-widget-policy-url input[type="radio"]',
|
||||
maybeShowNotice
|
||||
);
|
||||
$document.on( 'widget-updated widget-added', function ( e, widget ) {
|
||||
var widgetId = $( widget ).attr( 'id' );
|
||||
if ( widgetId && widgetId.indexOf( 'eu_cookie_law_widget' ) !== -1 ) {
|
||||
maybeShowNotice( null, $( '#' + widgetId + ' .eu-cookie-law-widget-policy-url' ) );
|
||||
}
|
||||
} );
|
||||
$( '.eu-cookie-law-widget-policy-url' ).each( maybeShowNotice );
|
||||
} );
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,97 @@
|
||||
( function () {
|
||||
var overlay = document.getElementById( 'eu-cookie-law' ),
|
||||
widget = document.querySelector( '.widget_eu_cookie_law_widget' );
|
||||
|
||||
if ( null === widget || null === overlay ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cookieValue = document.cookie.replace(
|
||||
/(?:(?:^|.*;\s*)eucookielaw\s*=\s*([^;]*).*$)|^.*$/,
|
||||
'$1'
|
||||
),
|
||||
inCustomizer = widget && widget.hasAttribute( 'data-customize-widget-id' ),
|
||||
getScrollTop,
|
||||
initialScrollPosition,
|
||||
scrollFunction;
|
||||
|
||||
/**
|
||||
* Gets the amount that the window is scrolled.
|
||||
*
|
||||
* @return int The distance from the top of the document.
|
||||
*/
|
||||
getScrollTop = function () {
|
||||
return Math.abs( document.body.getBoundingClientRect().y );
|
||||
};
|
||||
|
||||
if ( overlay.classList.contains( 'top' ) ) {
|
||||
widget.classList.add( 'top' );
|
||||
}
|
||||
|
||||
if ( overlay.classList.contains( 'ads-active' ) ) {
|
||||
var adsCookieValue = document.cookie.replace(
|
||||
/(?:(?:^|.*;\s*)personalized-ads-consent\s*=\s*([^;]*).*$)|^.*$/,
|
||||
'$1'
|
||||
);
|
||||
if ( '' !== cookieValue && '' !== adsCookieValue && ! inCustomizer ) {
|
||||
overlay.parentNode.removeChild( overlay );
|
||||
}
|
||||
} else if ( '' !== cookieValue && ! inCustomizer ) {
|
||||
overlay.parentNode.removeChild( overlay );
|
||||
}
|
||||
|
||||
document.body.appendChild( widget );
|
||||
overlay.querySelector( 'form' ).addEventListener( 'submit', accept );
|
||||
|
||||
if ( overlay.classList.contains( 'hide-on-scroll' ) ) {
|
||||
initialScrollPosition = getScrollTop();
|
||||
scrollFunction = function () {
|
||||
if ( Math.abs( getScrollTop() - initialScrollPosition ) > 50 ) {
|
||||
accept();
|
||||
}
|
||||
};
|
||||
window.addEventListener( 'scroll', scrollFunction );
|
||||
} else if ( overlay.classList.contains( 'hide-on-time' ) ) {
|
||||
setTimeout( accept, overlay.getAttribute( 'data-hide-timeout' ) * 1000 );
|
||||
}
|
||||
|
||||
var accepted = false;
|
||||
function accept( event ) {
|
||||
if ( accepted ) {
|
||||
return;
|
||||
}
|
||||
accepted = true;
|
||||
|
||||
if ( event && event.preventDefault ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if ( overlay.classList.contains( 'hide-on-scroll' ) ) {
|
||||
window.removeEventListener( 'scroll', scrollFunction );
|
||||
}
|
||||
|
||||
var expireTime = new Date();
|
||||
expireTime.setTime(
|
||||
expireTime.getTime() + overlay.getAttribute( 'data-consent-expiration' ) * 24 * 60 * 60 * 1000
|
||||
);
|
||||
|
||||
document.cookie =
|
||||
'eucookielaw=' + expireTime.getTime() + ';path=/;expires=' + expireTime.toGMTString();
|
||||
if (
|
||||
overlay.classList.contains( 'ads-active' ) &&
|
||||
overlay.classList.contains( 'hide-on-button' )
|
||||
) {
|
||||
document.cookie =
|
||||
'personalized-ads-consent=' +
|
||||
expireTime.getTime() +
|
||||
';path=/;expires=' +
|
||||
expireTime.toGMTString();
|
||||
}
|
||||
|
||||
overlay.classList.add( 'hide' );
|
||||
setTimeout( function () {
|
||||
overlay.parentNode.removeChild( overlay );
|
||||
widget.parentNode.removeChild( widget );
|
||||
}, 400 );
|
||||
}
|
||||
} )();
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
/**
|
||||
* EU Cookie Law Widget form.
|
||||
*
|
||||
* @html-template Jetpack_EU_Cookie_Law_Widget::form
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Redirect;
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Banner text', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['text'], 'default' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'text' ) ); ?>"
|
||||
type="radio"
|
||||
value="default"
|
||||
/>
|
||||
<?php esc_html_e( 'Default', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['text'], 'custom' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'text' ) ); ?>"
|
||||
type="radio"
|
||||
value="custom"
|
||||
/>
|
||||
<?php esc_html_e( 'Custom:', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<textarea
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'customtext' ) ); ?>"
|
||||
placeholder="<?php echo esc_attr( $instance['default-text'] ); ?>"
|
||||
><?php echo esc_html( $instance['customtext'] ); ?></textarea>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Privacy Policy Link', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul class="eu-cookie-law-widget-policy-url">
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['policy-url'], 'default' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'policy-url' ) ); ?>"
|
||||
type="radio"
|
||||
value="default"
|
||||
/>
|
||||
<?php esc_html_e( 'Default', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['policy-url'], 'custom' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'policy-url' ) ); ?>"
|
||||
type="radio"
|
||||
value="custom"
|
||||
/>
|
||||
<?php esc_html_e( 'Custom URL:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'custom-policy-url' ) ); ?>"
|
||||
placeholder="<?php echo esc_url( $instance['default-policy-url'] ); ?>"
|
||||
style="margin-top: .5em;"
|
||||
type="text"
|
||||
value="<?php echo esc_url( $instance['custom-policy-url'] ); ?>"
|
||||
/>
|
||||
<span class="notice notice-warning default-policy" style="display: none;">
|
||||
<span style="display: block; margin: .5em 0;">
|
||||
<strong><?php esc_html_e( 'Caution:', 'jetpack' ); ?></strong>
|
||||
<?php esc_html_e( 'The default policy URL only covers cookies set by Jetpack. If you’re running other plugins, custom cookies, or third-party tracking technologies, you should create and link to your own cookie statement.', 'jetpack' ); ?>
|
||||
</span>
|
||||
</span>
|
||||
<?php if ( Jetpack::is_module_active( 'wordads' ) ) : ?>
|
||||
<span class="notice notice-warning custom-policy" style="display: none;">
|
||||
<span style="display: block; margin: .5em 0;">
|
||||
<strong><?php esc_html_e( 'Caution:', 'jetpack' ); ?></strong>
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* Translators: %s is the URL to a Jetpack support article. */
|
||||
__( 'For GDPR compliance, please make sure your policy contains <a href="%s" target="_blank">privacy information relating to WordAds</a>.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( Redirect::get_url( 'jetpack-support-ads', array( 'anchor' => 'privacy' ) ) )
|
||||
);
|
||||
?>
|
||||
</span>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Link text', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<label>
|
||||
<input
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'policy-link-text' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['policy-link-text'] ); ?>"
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Button text', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<label>
|
||||
<input
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'button' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['button'] ); ?>"
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_x( 'Capture consent & hide the banner', 'action', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['hide'], 'button' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'hide' ) ); ?>"
|
||||
type="radio"
|
||||
value="button"
|
||||
<?php echo Jetpack::is_module_active( 'wordads' ) ? 'disabled' : ''; ?>
|
||||
/>
|
||||
<?php esc_html_e( 'after the user clicks the dismiss button', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['hide'], 'scroll' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'hide' ) ); ?>"
|
||||
type="radio"
|
||||
value="scroll"
|
||||
<?php echo Jetpack::is_module_active( 'wordads' ) ? 'disabled' : ''; ?>
|
||||
/>
|
||||
<?php esc_html_e( 'after the user scrolls the page', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['hide'], 'time' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'hide' ) ); ?>"
|
||||
type="radio"
|
||||
value="time"
|
||||
<?php echo Jetpack::is_module_active( 'wordads' ) ? 'disabled' : ''; ?>
|
||||
/>
|
||||
<?php esc_html_e( 'after this amount of time', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
max="1000"
|
||||
min="3"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'hide-timeout' ) ); ?>"
|
||||
style="padding: 3px 5px; width: 3em;"
|
||||
type="number"
|
||||
value="<?php echo esc_attr( $instance['hide-timeout'] ); ?>"
|
||||
/>
|
||||
<?php esc_html_e( 'seconds', 'jetpack' ); ?>
|
||||
</li>
|
||||
</ul>
|
||||
<?php if ( Jetpack::is_module_active( 'wordads' ) ) : ?>
|
||||
<span class="notice notice-warning" style="display: block;">
|
||||
<span style="display: block; margin: .5em 0;">
|
||||
<?php esc_html_e( 'Visitors must provide consent by clicking the dismiss button when WordAds is turned on.', 'jetpack' ); ?>
|
||||
</span>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_x( 'Consent expires after', 'action', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<input
|
||||
max="365"
|
||||
min="1"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'consent-expiration' ) ); ?>"
|
||||
style="padding: 3px 5px; width: 3.75em;"
|
||||
type="number"
|
||||
value="<?php echo esc_attr( $instance['consent-expiration'] ); ?>"
|
||||
/>
|
||||
<?php esc_html_e( 'days', 'jetpack' ); ?>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Color scheme', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['color-scheme'], 'default' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'color-scheme' ) ); ?>"
|
||||
type="radio"
|
||||
value="default"
|
||||
/>
|
||||
<?php esc_html_e( 'Light', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['color-scheme'], 'negative' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'color-scheme' ) ); ?>"
|
||||
type="radio"
|
||||
value="negative"
|
||||
/>
|
||||
<?php esc_html_e( 'Dark', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Position', 'jetpack' ); ?>
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['position'], 'bottom' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'position' ) ); ?>"
|
||||
type="radio"
|
||||
value="bottom"
|
||||
/>
|
||||
<?php esc_html_e( 'Bottom', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['position'], 'top' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'position' ) ); ?>"
|
||||
type="radio"
|
||||
value="top"
|
||||
/>
|
||||
<?php esc_html_e( 'Top', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p class="small">
|
||||
<?php esc_html_e( 'It is your own responsibility to ensure that your site complies with the relevant laws.', 'jetpack' ); ?>
|
||||
<a href="<?php echo esc_url( Redirect::get_url( 'jetpack-support-extra-sidebar-widgets-eu-cookie-law-widget' ) ); ?>">
|
||||
<?php esc_html_e( 'Click here for more information', 'jetpack' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
@@ -0,0 +1,134 @@
|
||||
.widget_eu_cookie_law_widget {
|
||||
animation: fadeIn 800ms;
|
||||
border: none;
|
||||
bottom: 1em;
|
||||
left: 1em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
right: 1em;
|
||||
width: auto;
|
||||
z-index: 50001;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; visibility: hidden; }
|
||||
to { opacity: 1; visibility: visible; }
|
||||
}
|
||||
|
||||
.widget_eu_cookie_law_widget.widget.top {
|
||||
bottom: auto;
|
||||
top: 1em;
|
||||
}
|
||||
|
||||
.admin-bar .widget_eu_cookie_law_widget.widget.top {
|
||||
top: 3em;
|
||||
}
|
||||
|
||||
amp-consent.widget_eu_cookie_law_widget.widget.top {
|
||||
top: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.admin-bar amp-consent.widget_eu_cookie_law_widget.widget.top {
|
||||
top: 0;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
#eu-cookie-law {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dedede;
|
||||
color: #2e4467;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
padding: 6px 6px 6px 15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#eu-cookie-law a,
|
||||
#eu-cookie-law a:active,
|
||||
#eu-cookie-law a:visited {
|
||||
color: inherit;
|
||||
cursor: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#eu-cookie-law a:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#eu-cookie-law.negative {
|
||||
background-color: #000;
|
||||
border: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#eu-cookie-law.hide {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 400ms, visibility 400ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using a highly-specific rule to make sure that certain form styles
|
||||
* will be reset
|
||||
*/
|
||||
#eu-cookie-law form {
|
||||
margin-bottom: 0;
|
||||
position: static;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using a highly-specific rule to make sure that all button styles
|
||||
* will be reset
|
||||
*/
|
||||
#eu-cookie-law input,
|
||||
#eu-cookie-law input:hover,
|
||||
#eu-cookie-law input:focus {
|
||||
background: #f3f3f3;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
color: #2e4453;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
float: right;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0 0 0 5%;
|
||||
padding: 8px 12px;
|
||||
position: static;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#eu-cookie-law.negative input,
|
||||
#eu-cookie-law.negative input:hover,
|
||||
#eu-cookie-law.negative input:focus {
|
||||
background: #282828;
|
||||
border-color: #535353;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media ( max-width: 600px ) {
|
||||
#eu-cookie-law {
|
||||
padding-bottom: 55px;
|
||||
}
|
||||
#eu-cookie-law input.accept {
|
||||
bottom: 8px;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tweak position of the Customizer edit button to make it more obvious this
|
||||
* is an editable widget rather than a normal accept cookies banner.
|
||||
*/
|
||||
.widget_eu_cookie_law_widget .customize-partial-edit-shortcut > button {
|
||||
left: 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* AMP Widget for Cookies and Consent.
|
||||
*
|
||||
* @html-template Jetpack_EU_Cookie_Law_Widget::widget
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
|
||||
<amp-consent id="eu-cookie-consent" layout="nodisplay" class="widget widget_eu_cookie_law_widget<?php echo esc_attr( ! empty( $instance['position'] ) && 'top' === $instance['position'] ? ' top' : '' ); ?>">
|
||||
<script type="application/json">
|
||||
{
|
||||
"consentInstanceId": "eu-cookie-consent",
|
||||
"consentRequired": true,
|
||||
"promptUI": "eu-cookie-consent-prompt"
|
||||
}
|
||||
</script>
|
||||
<div class="popupOverlay" id="eu-cookie-consent-prompt">
|
||||
<div class="consentPopup<?php echo esc_attr( ! empty( $instance['color-scheme'] ) && 'negative' === $instance['color-scheme'] ? ' negative' : '' ); ?>" id="eu-cookie-law">
|
||||
<form>
|
||||
<input type="button" on="tap:eu-cookie-consent.accept" class="accept" value="<?php echo esc_attr( $instance['button'] ); ?>" />
|
||||
</form>
|
||||
<?php
|
||||
if ( 'default' === $instance['text'] || empty( $instance['customtext'] ) ) {
|
||||
echo wp_kses_post( nl2br( $instance['default-text'] ) );
|
||||
} else {
|
||||
echo esc_html( $instance['customtext'] );
|
||||
}
|
||||
|
||||
$is_default_policy = 'default' === $instance['policy-url'] || empty( $instance['custom-policy-url'] );
|
||||
$policy_link_url = $is_default_policy ? $instance['default-policy-url'] : $instance['custom-policy-url'];
|
||||
$policy_link_rel = $is_default_policy ? 'nofollow' : '';
|
||||
?>
|
||||
<a href="<?php echo esc_url( $policy_link_url ); ?>" rel="<?php echo esc_attr( $policy_link_rel ); ?>">
|
||||
<?php echo esc_html( $instance['policy-link-text'] ); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</amp-consent>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Widget for Cookies and Consent.
|
||||
*
|
||||
* @html-template Jetpack_EU_Cookie_Law_Widget::widget
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
|
||||
<div
|
||||
class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
|
||||
data-hide-timeout="<?php echo (int) $instance['hide-timeout']; ?>"
|
||||
data-consent-expiration="<?php echo (int) $instance['consent-expiration']; ?>"
|
||||
id="eu-cookie-law"
|
||||
>
|
||||
<form method="post" id="jetpack-eu-cookie-law-form">
|
||||
<input type="submit" value="<?php echo esc_attr( $instance['button'] ); ?>" class="accept" />
|
||||
</form>
|
||||
|
||||
<?php
|
||||
if ( 'default' === $instance['text'] || empty( $instance['customtext'] ) ) {
|
||||
echo nl2br( esc_html( $instance['default-text'] ) );
|
||||
} else {
|
||||
echo nl2br( esc_html( $instance['customtext'] ) );
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
$is_default_policy = 'default' === $instance['policy-url'] || empty( $instance['custom-policy-url'] );
|
||||
$policy_link_url = $is_default_policy ? $instance['default-policy-url'] : $instance['custom-policy-url'];
|
||||
$policy_link_rel = $is_default_policy ? 'nofollow' : '';
|
||||
?>
|
||||
<a href="<?php echo esc_url( $policy_link_url ); ?>" rel="<?php echo esc_attr( $policy_link_rel ); ?>">
|
||||
<?php echo esc_html( $instance['policy-link-text'] ); ?>
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,452 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_facebook_likebox_init' );
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets
|
||||
*/
|
||||
function jetpack_facebook_likebox_init() {
|
||||
register_widget( 'WPCOM_Widget_Facebook_LikeBox' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Facebook Page Plugin (formerly known as the Like Box)
|
||||
* Display a Facebook Page Plugin as a widget (replaces the old like box plugin)
|
||||
* https://developers.facebook.com/docs/plugins/page-plugin
|
||||
*/
|
||||
class WPCOM_Widget_Facebook_LikeBox extends WP_Widget {
|
||||
/**
|
||||
* Default height.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $default_height = 580;
|
||||
|
||||
/**
|
||||
* Default width.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $default_width = 340;
|
||||
|
||||
/**
|
||||
* Max width.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $max_width = 500;
|
||||
|
||||
/**
|
||||
* Min width.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $min_width = 180;
|
||||
|
||||
/**
|
||||
* Max height.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $max_height = 9999;
|
||||
|
||||
/**
|
||||
* Min height/
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $min_height = 130;
|
||||
|
||||
/**
|
||||
* WPCOM_Widget_Facebook_LikeBox constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'facebook-likebox',
|
||||
/**
|
||||
* Filter the name of a widget included in the Extra Sidebar Widgets module.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 2.1.2
|
||||
*
|
||||
* @param string $widget_name Widget title.
|
||||
*/
|
||||
apply_filters( 'jetpack_widget_name', __( 'Facebook Page Plugin', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_facebook_likebox',
|
||||
'description' => __( 'Use the Facebook Page Plugin to connect visitors to your Facebook Page', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script( 'jetpack-facebook-embed' );
|
||||
wp_enqueue_style(
|
||||
'jetpack_facebook_likebox',
|
||||
plugins_url( 'facebook-likebox/style.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
// Inline styles. @see wp_maybe_inline_styles()
|
||||
wp_style_add_data( 'jetpack_facebook_likebox', 'path', plugin_dir_path( __FILE__ ) . 'facebook-likebox/style.css' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the widget.
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$before_widget = isset( $args['before_widget'] ) ? $args['before_widget'] : '';
|
||||
$before_title = isset( $args['before_title'] ) ? $args['before_title'] : '';
|
||||
$after_title = isset( $args['after_title'] ) ? $args['after_title'] : '';
|
||||
$after_widget = isset( $args['after_widget'] ) ? $args['after_widget'] : '';
|
||||
$like_args = $this->get_default_args();
|
||||
|
||||
if ( isset( $instance['like_args'] ) ) {
|
||||
$like_args = $this->normalize_facebook_args( $instance['like_args'] );
|
||||
}
|
||||
|
||||
if ( empty( $like_args['href'] ) || ! $this->is_valid_facebook_url( $like_args['href'] ) ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo $before_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
$error_link = wp_kses(
|
||||
sprintf(
|
||||
/* translators: %s: link to widgets administration screen. */
|
||||
__( 'It looks like your Facebook URL is incorrectly configured. Please check it in your <a href="%1$s">widget settings</a>.', 'jetpack' ),
|
||||
esc_url( admin_url( 'widgets.php' ) )
|
||||
),
|
||||
array( 'a' => array( 'href' => array() ) )
|
||||
);
|
||||
printf(
|
||||
'<p>%s</p>',
|
||||
$error_link // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
|
||||
echo $after_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '<!-- Invalid Facebook Page URL -->';
|
||||
return;
|
||||
}
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
$page_url = set_url_scheme( $like_args['href'], 'https' );
|
||||
|
||||
$like_args['show_faces'] = (bool) $like_args['show_faces'] ? 'true' : 'false';
|
||||
$like_args['stream'] = (bool) $like_args['stream'] ? 'timeline' : 'false';
|
||||
$like_args['cover'] = (bool) $like_args['cover'] ? 'false' : 'true';
|
||||
$like_args['small_header'] = (bool) $like_args['small_header'] ? 'true' : 'false';
|
||||
|
||||
/**
|
||||
* Filter Facebook Likebox's widget call to action button
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 8.4.0
|
||||
*
|
||||
* @param bool True value hides the call to action button
|
||||
*/
|
||||
$hide_cta = apply_filters( 'jetpack_facebook_likebox_hide_cta', false );
|
||||
|
||||
echo $before_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( ! empty( $title ) ) :
|
||||
echo $before_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
$likebox_widget_title = '<a href="' . esc_url( $page_url ) . '">' . $title . '</a>';
|
||||
/**
|
||||
* Filter Facebook Likebox's widget title.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param string $likebox_widget_title Likebox Widget title (including a link to the Page URL).
|
||||
* @param string $title Widget title as set in the widget settings.
|
||||
* @param string $page_url Facebook Page URL.
|
||||
*/
|
||||
$likebox_widget_title = apply_filters( 'jetpack_facebook_likebox_title', $likebox_widget_title, $title, $page_url );
|
||||
|
||||
echo wp_kses(
|
||||
$likebox_widget_title,
|
||||
array( 'a' => array( 'href' => array() ) )
|
||||
);
|
||||
|
||||
echo $after_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
endif;
|
||||
|
||||
?>
|
||||
<div id="fb-root"></div>
|
||||
<div class="fb-page" data-href="<?php echo esc_url( $page_url ); ?>" data-width="<?php echo (int) $like_args['width']; ?>" data-height="<?php echo (int) $like_args['height']; ?>" data-hide-cover="<?php echo esc_attr( $like_args['cover'] ); ?>" data-show-facepile="<?php echo esc_attr( $like_args['show_faces'] ); ?>" data-tabs="<?php echo esc_attr( $like_args['stream'] ); ?>" data-hide-cta="<?php echo esc_attr( $hide_cta ? 'true' : 'false' ); ?>" data-small-header="<?php echo esc_attr( $like_args['small_header'] ); ?>">
|
||||
<div class="fb-xfbml-parse-ignore"><blockquote cite="<?php echo esc_url( $page_url ); ?>"><a href="<?php echo esc_url( $page_url ); ?>"><?php echo esc_html( $title ); ?></a></blockquote></div>
|
||||
</div>
|
||||
<?php
|
||||
wp_enqueue_script( 'jetpack-facebook-embed' );
|
||||
|
||||
echo $after_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'facebook-likebox' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update widget.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance New widget instance data.
|
||||
* @param array $old_instance Old widget instance data.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
$instance['title'] = trim( wp_strip_all_tags( stripslashes( $new_instance['title'] ) ) );
|
||||
|
||||
// Set up widget values.
|
||||
$instance['like_args'] = array(
|
||||
'href' => trim( wp_strip_all_tags( stripslashes( $new_instance['href'] ) ) ),
|
||||
'width' => (int) $new_instance['width'],
|
||||
'height' => (int) $new_instance['height'],
|
||||
'show_faces' => isset( $new_instance['show_faces'] ),
|
||||
'stream' => isset( $new_instance['stream'] ),
|
||||
'cover' => isset( $new_instance['cover'] ),
|
||||
'small_header' => isset( $new_instance['small_header'] ),
|
||||
);
|
||||
|
||||
$instance['like_args'] = $this->normalize_facebook_args( $instance['like_args'] );
|
||||
|
||||
// Include the new instance's args in the array's top level to support updating from the Widgets page.
|
||||
$instance = array_merge( $instance, array_intersect_key( $instance['like_args'], $new_instance ) );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget settings form.
|
||||
*
|
||||
* @param array $instance Current settings.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args(
|
||||
(array) $instance,
|
||||
array(
|
||||
'title' => '',
|
||||
'like_args' => $this->get_default_args(),
|
||||
)
|
||||
);
|
||||
$like_args = $this->normalize_facebook_args( $instance['like_args'] );
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
|
||||
<?php esc_html_e( 'Title', 'jetpack' ); ?>
|
||||
<input type="text" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat" />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'href' ) ); ?>">
|
||||
<?php esc_html_e( 'Facebook Page URL', 'jetpack' ); ?>
|
||||
<input type="text" name="<?php echo esc_attr( $this->get_field_name( 'href' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'href' ) ); ?>" value="<?php echo esc_url( $like_args['href'] ); ?>" class="widefat" />
|
||||
<br />
|
||||
<small><?php esc_html_e( 'The widget only works with Facebook Pages.', 'jetpack' ); ?></small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>">
|
||||
<?php esc_html_e( 'Width in pixels', 'jetpack' ); ?>
|
||||
<input type="number" class="smalltext" min="<?php echo esc_attr( $this->min_width ); ?>" max="<?php echo esc_attr( $this->max_width ); ?>" maxlength="3" name="<?php echo esc_attr( $this->get_field_name( 'width' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>" value="<?php echo esc_attr( $like_args['width'] ); ?>" style="text-align: center;" />
|
||||
<small>
|
||||
<?php
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
/* translators: %s is the minimum pixel width */
|
||||
__( 'Minimum: %s', 'jetpack' ),
|
||||
$this->min_width
|
||||
)
|
||||
);
|
||||
echo ' / ';
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
/* translators: %s is the maximum pixel width */
|
||||
__( 'Maximum: %s', 'jetpack' ),
|
||||
$this->max_width
|
||||
)
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>">
|
||||
<?php esc_html_e( 'Height in pixels', 'jetpack' ); ?>
|
||||
<input type="number" class="smalltext" min="<?php echo esc_attr( $this->min_height ); ?>" max="<?php echo esc_attr( $this->max_height ); ?>" maxlength="3" name="<?php echo esc_attr( $this->get_field_name( 'height' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>" value="<?php echo esc_attr( $like_args['height'] ); ?>" style="text-align: center;" />
|
||||
<small>
|
||||
<?php
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
/* translators: %s is the minimum pixel height */
|
||||
__( 'Minimum: %s', 'jetpack' ),
|
||||
$this->min_height
|
||||
)
|
||||
);
|
||||
echo ' / ';
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
/* translators: %s is the maximum pixel height */
|
||||
__( 'Maximum: %s', 'jetpack' ),
|
||||
$this->max_height
|
||||
)
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'show_faces' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'show_faces' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'show_faces' ) ); ?>" <?php checked( $like_args['show_faces'] ); ?> />
|
||||
<?php esc_html_e( 'Show Faces', 'jetpack' ); ?>
|
||||
<br />
|
||||
<small><?php esc_html_e( 'Show profile photos in the plugin.', 'jetpack' ); ?></small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'stream' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'stream' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'stream' ) ); ?>" <?php checked( $like_args['stream'] ); ?> />
|
||||
<?php esc_html_e( 'Show Timeline', 'jetpack' ); ?>
|
||||
<br />
|
||||
<small><?php esc_html_e( 'Show Page Posts.', 'jetpack' ); ?></small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'cover' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'cover' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'cover' ) ); ?>" <?php checked( $like_args['cover'] ); ?> />
|
||||
<?php esc_html_e( 'Show Cover Photo', 'jetpack' ); ?>
|
||||
<br />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'small_header' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'small_header' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'small_header' ) ); ?>" <?php checked( $like_args['small_header'] ); ?> />
|
||||
<?php esc_html_e( 'Use Small Header', 'jetpack' ); ?>
|
||||
<br />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Facebook Likebox default options.
|
||||
*/
|
||||
public function get_default_args() {
|
||||
$defaults = array(
|
||||
'href' => '',
|
||||
'width' => $this->default_width,
|
||||
'height' => $this->default_height,
|
||||
'show_faces' => 'true',
|
||||
'stream' => '',
|
||||
'cover' => 'true',
|
||||
'small_header' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter Facebook Likebox default options.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 1.3.1
|
||||
*
|
||||
* @param array $defaults Array of default options.
|
||||
*/
|
||||
return apply_filters( 'jetpack_facebook_likebox_defaults', $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the Facebook Likebox options.
|
||||
*
|
||||
* @param array $args Array of arguments.
|
||||
*/
|
||||
public function normalize_facebook_args( $args ) {
|
||||
$args = wp_parse_args( (array) $args, $this->get_default_args() );
|
||||
|
||||
// Validate the Facebook Page URL.
|
||||
if ( $this->is_valid_facebook_url( $args['href'] ) ) {
|
||||
$temp = explode( '?', $args['href'] );
|
||||
$args['href'] = str_replace( array( 'http://facebook.com', 'https://facebook.com' ), array( 'http://www.facebook.com', 'https://www.facebook.com' ), $temp[0] );
|
||||
} else {
|
||||
$args['href'] = '';
|
||||
}
|
||||
|
||||
$args['width'] = $this->normalize_int_value( (int) $args['width'], $this->max_width, $this->min_width );
|
||||
$args['height'] = $this->normalize_int_value( (int) $args['height'], $this->max_height, $this->min_height );
|
||||
$args['show_faces'] = (bool) $args['show_faces'];
|
||||
$args['stream'] = (bool) $args['stream'];
|
||||
$args['cover'] = (bool) $args['cover'];
|
||||
$args['small_header'] = (bool) $args['small_header'];
|
||||
|
||||
// The height used to be dependent on other widget settings
|
||||
// If the user changes those settings but doesn't customize the height,
|
||||
// let's intelligently assign a new height.
|
||||
if ( in_array( $args['height'], array( 580, 110, 432 ), true ) ) {
|
||||
if ( $args['show_faces'] && $args['stream'] ) {
|
||||
$args['height'] = 580;
|
||||
} elseif ( ! $args['show_faces'] && ! $args['stream'] ) {
|
||||
$args['height'] = 130;
|
||||
} else {
|
||||
$args['height'] = 432;
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if URL is a valid Facebook Page URL.
|
||||
*
|
||||
* @param string $url URL to check.
|
||||
*/
|
||||
public function is_valid_facebook_url( $url ) {
|
||||
return str_contains( $url, 'facebook.com' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an integer value within a given range.
|
||||
*
|
||||
* @param int $value Value to normalize.
|
||||
* @param int $max Maximum value.
|
||||
* @param int $min Minimum value.
|
||||
*
|
||||
* @return int Normalized value.
|
||||
*/
|
||||
public function normalize_int_value( $value, $max = 0, $min = 0 ) {
|
||||
$value = (int) $value;
|
||||
|
||||
if ( $value > $max ) {
|
||||
$value = $max;
|
||||
} elseif ( $value < $min ) {
|
||||
$value = $min;
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.widget_facebook_likebox {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
|
||||
/**
|
||||
* Disable direct access/execution to/of the widget code.
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Jetpack_Flickr_Widget' ) ) {
|
||||
/**
|
||||
* Flickr Widget
|
||||
*
|
||||
* Display your recent Flickr photos.
|
||||
*/
|
||||
class Jetpack_Flickr_Widget extends WP_Widget {
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'flickr',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Flickr', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => esc_html__( 'Display your recent Flickr photos.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
),
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue style.
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_enqueue_style( 'flickr-widget-style', plugins_url( 'flickr/style.css', __FILE__ ), array(), '20170405' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values.
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Default values for the widget options.
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'title' => esc_html__( 'Flickr Photos', 'jetpack' ),
|
||||
'items' => 4,
|
||||
'target' => false,
|
||||
'flickr_image_size' => 'thumbnail',
|
||||
'flickr_rss_url' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of the widget.
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
* @html-template-var string|null $flickr_home
|
||||
* @html-template-var string $photos';
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
if ( ! empty( $instance['flickr_rss_url'] ) ) {
|
||||
/*
|
||||
* Parse the URL, and rebuild a URL that's sure to display images.
|
||||
* Some Flickr Feeds do not display images by default.
|
||||
*/
|
||||
$flickr_parameters = wp_parse_url( htmlspecialchars_decode( $instance['flickr_rss_url'], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) );
|
||||
|
||||
// Is it a Flickr Feed.
|
||||
if (
|
||||
! empty( $flickr_parameters['host'] )
|
||||
&& ! empty( $flickr_parameters['query'] )
|
||||
&& str_contains( $flickr_parameters['host'], 'flickr' )
|
||||
) {
|
||||
parse_str( $flickr_parameters['query'], $vars );
|
||||
|
||||
// Do we have an ID in the feed? Let's continue.
|
||||
if ( isset( $vars['id'] ) ) {
|
||||
|
||||
// Flickr Feeds can be used for groups or for individuals.
|
||||
if (
|
||||
! empty( $flickr_parameters['path'] )
|
||||
&& str_contains( $flickr_parameters['path'], 'groups' )
|
||||
) {
|
||||
$feed_url = 'https://api.flickr.com/services/feeds/groups_pool.gne';
|
||||
} else {
|
||||
$feed_url = 'https://api.flickr.com/services/feeds/photos_public.gne';
|
||||
}
|
||||
|
||||
// Build our new RSS feed.
|
||||
$rss_url = sprintf(
|
||||
'%1$s?id=%2$s&format=rss_200_enc',
|
||||
esc_url( $feed_url ),
|
||||
esc_attr( $vars['id'] )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Still no RSS feed URL? Get a default feed from Flickr to grab interesting photos.
|
||||
if ( empty( $rss_url ) ) {
|
||||
$rss_url = 'https://api.flickr.com/services/feeds/photos_interesting.gne?format=rss_200';
|
||||
}
|
||||
|
||||
$rss = fetch_feed( $rss_url );
|
||||
|
||||
$photos = '';
|
||||
$flickr_home = null; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Used in flickr/widget.php template file.
|
||||
if ( ! is_wp_error( $rss ) ) {
|
||||
foreach ( $rss->get_items( 0, $instance['items'] ) as $photo ) {
|
||||
switch ( $instance['flickr_image_size'] ) {
|
||||
case 'thumbnail':
|
||||
$src = $photo->get_enclosure()->get_thumbnail();
|
||||
break;
|
||||
case 'small':
|
||||
$src = preg_match( '/src="(.*?)"/i', $photo->get_description(), $p );
|
||||
$src = $p[1];
|
||||
break;
|
||||
case 'large':
|
||||
$src = $photo->get_enclosure()->get_link();
|
||||
break;
|
||||
}
|
||||
|
||||
$photos .= '<a href="' . esc_url( $photo->get_permalink(), array( 'http', 'https' ) ) . '" ';
|
||||
if ( $instance['target'] ) {
|
||||
$photos .= 'target="_blank" rel="noopener noreferrer" ';
|
||||
}
|
||||
$photos .= '><img src="' . esc_url( $src, array( 'http', 'https' ) ) . '" ';
|
||||
$photos .= 'alt="' . esc_attr( $photo->get_title() ) . '" ';
|
||||
$photos .= 'title="' . esc_attr( $photo->get_title() ) . '" ';
|
||||
$photos .= ' /></a>';
|
||||
}
|
||||
if ( ! empty( $photos ) ) {
|
||||
$photos = apply_filters( 'jetpack_image_cdn_content', $photos );
|
||||
}
|
||||
|
||||
$flickr_home = $rss->get_link(); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Used in flickr/widget.php template file.
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( empty( $photos ) ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
printf(
|
||||
'<p>%1$s<br />%2$s</p>',
|
||||
esc_html__( 'There are no photos to display. Make sure your Flickr feed URL is correct, and that your pictures are publicly accessible.', 'jetpack' ),
|
||||
esc_html__( '(Only admins can see this message)', 'jetpack' )
|
||||
);
|
||||
}
|
||||
} else {
|
||||
echo $args['before_title'] . $instance['title'] . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
require __DIR__ . '/flickr/widget.php';
|
||||
}
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'flickr' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-end widget form.
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
require __DIR__ . '/flickr/form.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
if ( isset( $new_instance['title'] ) ) {
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['items'] ) ) {
|
||||
$instance['items'] = (int) $new_instance['items'];
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['target'] ) ) {
|
||||
$instance['target'] = (bool) $new_instance['target'];
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $new_instance['flickr_image_size'] ) &&
|
||||
in_array( $new_instance['flickr_image_size'], array( 'thumbnail', 'small', 'large' ), true )
|
||||
) {
|
||||
$instance['flickr_image_size'] = $new_instance['flickr_image_size'];
|
||||
} else {
|
||||
$instance['flickr_image_size'] = 'thumbnail';
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['flickr_rss_url'] ) ) {
|
||||
$instance['flickr_rss_url'] = esc_url( $new_instance['flickr_rss_url'], array( 'http', 'https' ) );
|
||||
|
||||
if ( strlen( $instance['flickr_rss_url'] ) < 10 ) {
|
||||
$instance['flickr_rss_url'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Jetpack_Flickr_Widget widget.
|
||||
*/
|
||||
function jetpack_register_flickr_widget() {
|
||||
register_widget( 'Jetpack_Flickr_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_register_flickr_widget' );
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* Jetpack_Flickr_Widget settings form output.
|
||||
*
|
||||
* @html-template Jetpack_Flickr_Widget::form
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Title:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['title'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Flickr RSS URL:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'flickr_rss_url' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['flickr_rss_url'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
<?php esc_html_e( 'To find your Flickr RSS URL, go to your photostream, add "?details=1" to the URL, and hit enter. Scroll down until you see the RSS icon or the "Latest" link. Right-click on either options and copy the URL. Paste into the box above.', 'jetpack' ); ?>
|
||||
</small>
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* Translators: %s is the URL to an example Flickr RSS feed. */
|
||||
__( 'Leave the Flickr RSS URL field blank to display <a target="_blank" href="%s">interesting</a> Flickr photos.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
'https://www.flickr.com/explore/interesting'
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'How many photos would you like to display?', 'jetpack' ); ?>
|
||||
</label>
|
||||
<select name="<?php echo esc_attr( $this->get_field_name( 'items' ) ); ?>">
|
||||
<?php for ( $i = 1; $i <= 10; ++$i ) { ?>
|
||||
<option
|
||||
<?php selected( $instance['items'], $i ); ?>
|
||||
value="<?php echo esc_attr( $i ); ?>"
|
||||
>
|
||||
<?php echo esc_html( $i ); ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'target' ) ); ?>"
|
||||
<?php checked( $instance['target'] ); ?>
|
||||
/>
|
||||
<?php esc_html_e( 'Open images in new tab?', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<div>
|
||||
<?php esc_html_e( 'What size photos would you like to display?', 'jetpack' ); ?>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['flickr_image_size'], 'thumbnail' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'flickr_image_size' ) ); ?>"
|
||||
type="radio"
|
||||
value="thumbnail"
|
||||
/>
|
||||
<?php esc_html_e( 'Thumbnail', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['flickr_image_size'], 'small' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'flickr_image_size' ) ); ?>"
|
||||
type="radio"
|
||||
value="small"
|
||||
/>
|
||||
<?php esc_html_e( 'Medium', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['flickr_image_size'], 'large' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'flickr_image_size' ) ); ?>"
|
||||
type="radio"
|
||||
value="large"
|
||||
/>
|
||||
<?php esc_html_e( 'Large', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
@@ -0,0 +1,16 @@
|
||||
.flickr-images {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flickr-size-thumbnail .flickr-images {
|
||||
align-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flickr-images img {
|
||||
max-width: 100%;
|
||||
margin: 5px;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Jetpack_Flickr_Widget frontend widget output.
|
||||
*
|
||||
* @html-template Jetpack_Flickr_Widget::widget
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<!-- Start of Flickr Widget -->
|
||||
<div class="flickr-wrapper flickr-size-<?php echo esc_attr( $instance['flickr_image_size'] ); ?>">
|
||||
<div class="flickr-images">
|
||||
<?php echo $photos; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping handled in Jetpack_Flickr_Widget class. ?>
|
||||
</div>
|
||||
|
||||
<?php if ( isset( $flickr_home ) ) { ?>
|
||||
<a class="flickr-more" href="<?php echo esc_url( $flickr_home, array( 'http', 'https' ) ); ?>">
|
||||
<?php esc_html_e( 'More Photos', 'jetpack' ); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<!-- End of Flickr Widget -->
|
||||
@@ -0,0 +1,521 @@
|
||||
<?php // phpcs:ignore eWordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Module Name: Gallery widget
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
|
||||
|
||||
/**
|
||||
* Jetpack_Gallery_Widget main class.
|
||||
*/
|
||||
class Jetpack_Gallery_Widget extends WP_Widget {
|
||||
const THUMB_SIZE = 45;
|
||||
const DEFAULT_WIDTH = 265;
|
||||
|
||||
/**
|
||||
* The width of the gallery widget.
|
||||
* May be customized by the 'gallery_widget_content_width' filter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $instance_width;
|
||||
|
||||
/**
|
||||
* Jetpack_Gallery_Widget constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget-gallery',
|
||||
'description' => __( 'Display a photo gallery or slideshow', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
);
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
|
||||
parent::__construct(
|
||||
'gallery',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Gallery', 'jetpack' ) ),
|
||||
$widget_ops
|
||||
);
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
|
||||
|
||||
if ( class_exists( 'Jetpack_Tiled_Gallery' ) ) {
|
||||
add_action( 'wp_enqueue_scripts', array( 'Jetpack_Tiled_Gallery', 'default_scripts_and_styles' ) );
|
||||
}
|
||||
|
||||
if ( class_exists( 'Jetpack_Slideshow_Shortcode' ) ) {
|
||||
$slideshow = new Jetpack_Slideshow_Shortcode();
|
||||
add_action( 'wp_enqueue_scripts', array( $slideshow, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
if ( class_exists( 'Jetpack_Carousel' ) ) {
|
||||
$carousel = new Jetpack_Carousel();
|
||||
add_action( 'wp_enqueue_scripts', array( $carousel, 'enqueue_assets' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the widget.
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults() );
|
||||
|
||||
$this->enqueue_frontend_scripts();
|
||||
|
||||
$before_widget = isset( $args['before_widget'] ) ? $args['before_widget'] : '';
|
||||
$before_title = isset( $args['before_title'] ) ? $args['before_title'] : '';
|
||||
$after_title = isset( $args['after_title'] ) ? $args['after_title'] : '';
|
||||
$after_widget = isset( $args['after_widget'] ) ? $args['after_widget'] : '';
|
||||
|
||||
$instance['attachments'] = $this->get_attachments( $instance );
|
||||
|
||||
$classes = array();
|
||||
|
||||
$classes[] = 'widget-gallery-' . $instance['type'];
|
||||
|
||||
/*
|
||||
* Due to a bug in the carousel plugin,
|
||||
* carousels will be triggered for all tiled galleries that exist on a page with other tiled galleries,
|
||||
* regardless of whether or not the widget was set to Carousel mode.
|
||||
* The onClick selector is simply too broad, since it was not written with widgets in mind.
|
||||
* This special class prevents that behavior, via an override handler in gallery.js.
|
||||
*/
|
||||
if ( 'carousel' !== $instance['link'] && 'slideshow' !== $instance['type'] ) {
|
||||
$classes[] = 'no-carousel';
|
||||
} else {
|
||||
$classes[] = 'carousel';
|
||||
}
|
||||
|
||||
$classes = implode( ' ', $classes );
|
||||
|
||||
if ( 'carousel' === $instance['link'] ) {
|
||||
require_once plugin_dir_path( realpath( __DIR__ . '/../carousel/jetpack-carousel.php' ) ) . 'jetpack-carousel.php';
|
||||
|
||||
if ( class_exists( 'Jetpack_Carousel' ) ) {
|
||||
// Create new carousel so we can use the enqueue_assets() method. Not ideal, but there is a decent amount
|
||||
// of logic in that method that shouldn't be duplicated.
|
||||
$carousel = new Jetpack_Carousel();
|
||||
|
||||
$carousel->enqueue_assets();
|
||||
}
|
||||
}
|
||||
|
||||
echo $before_widget . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
|
||||
if ( $title ) {
|
||||
echo $before_title . $title . $after_title . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
echo '<div class="' . esc_attr( $classes ) . '">' . "\n";
|
||||
|
||||
$method = $instance['type'] . '_widget';
|
||||
|
||||
/**
|
||||
* Allow the width of a gallery to be altered by themes or other code.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 2.5.0
|
||||
*
|
||||
* @param int self::DEFAULT_WIDTH Default gallery width. Default is 265.
|
||||
* @param string $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
$this->instance_width = apply_filters( 'gallery_widget_content_width', self::DEFAULT_WIDTH, $args, $instance );
|
||||
|
||||
// Register a filter to modify the tiled_gallery_content_width, so Jetpack_Tiled_Gallery
|
||||
// can appropriately size the tiles.
|
||||
add_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
|
||||
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
echo $this->$method( $args, $instance ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
// Remove the stored $instance_width, as it is no longer needed.
|
||||
$this->instance_width = null;
|
||||
|
||||
// Remove the filter, so any Jetpack_Tiled_Gallery in a post is not affected.
|
||||
remove_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
|
||||
|
||||
echo "\n" . '</div>'; // .widget-gallery-$type
|
||||
|
||||
echo "\n" . $after_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'gallery' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the images attached to the gallery Widget
|
||||
*
|
||||
* @param array $instance The Widget instance for which you'd like attachments.
|
||||
* @return array Array of attachment objects for the Widget in $instance
|
||||
*/
|
||||
public function get_attachments( $instance ) {
|
||||
$ids = explode( ',', $instance['ids'] );
|
||||
|
||||
if ( isset( $instance['random'] ) && 'on' === $instance['random'] ) {
|
||||
shuffle( $ids );
|
||||
}
|
||||
|
||||
$attachments_query = new WP_Query(
|
||||
array(
|
||||
'post__in' => $ids,
|
||||
'post_status' => 'inherit',
|
||||
'post_type' => 'attachment',
|
||||
'post_mime_type' => 'image',
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'post__in',
|
||||
)
|
||||
);
|
||||
|
||||
$attachments = $attachments_query->get_posts();
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML for a rectangular, tiled Widget
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The Widget instance to generate HTML for.
|
||||
* @return string String of HTML representing a rectangular gallery
|
||||
*/
|
||||
public function rectangular_widget( $args, $instance ) {
|
||||
if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
|
||||
&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Rectangular' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Jetpack_Tiled_Gallery::default_scripts_and_styles();
|
||||
|
||||
$layout = new Jetpack_Tiled_Gallery_Layout_Rectangular( $instance['attachments'], $instance['link'], false, 3 );
|
||||
return $layout->HTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML for a square (grid style) Widget
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The Widget instance to generate HTML for.
|
||||
* @return string String of HTML representing a square gallery
|
||||
*/
|
||||
public function square_widget( $args, $instance ) {
|
||||
if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
|
||||
&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Square' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Jetpack_Tiled_Gallery::default_scripts_and_styles();
|
||||
|
||||
$layout = new Jetpack_Tiled_Gallery_Layout_Square( $instance['attachments'], $instance['link'], false, 3 );
|
||||
return $layout->HTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML for a circular (grid style) Widget
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The Widget instance to generate HTML for.
|
||||
* @return string String of HTML representing a circular gallery
|
||||
*/
|
||||
public function circle_widget( $args, $instance ) {
|
||||
if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
|
||||
&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Circle' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Jetpack_Tiled_Gallery::default_scripts_and_styles();
|
||||
|
||||
$layout = new Jetpack_Tiled_Gallery_Layout_Circle( $instance['attachments'], $instance['link'], false, 3 );
|
||||
return $layout->HTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML for a slideshow Widget
|
||||
*
|
||||
* @todo Is slideshow_widget() still used?
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The Widget instance to generate HTML for.
|
||||
* @return string String of HTML representing a slideshow gallery
|
||||
*/
|
||||
public function slideshow_widget( $args, $instance ) {
|
||||
global $content_width;
|
||||
|
||||
require_once plugin_dir_path( realpath( __DIR__ . '/../shortcodes/slideshow.php' ) ) . 'slideshow.php';
|
||||
|
||||
if ( ! class_exists( 'Jetpack_Slideshow_Shortcode' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count( $instance['attachments'] ) < 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slideshow = new Jetpack_Slideshow_Shortcode();
|
||||
|
||||
$slideshow->enqueue_scripts();
|
||||
|
||||
$gallery_instance = 'widget-' . $args['widget_id'];
|
||||
|
||||
$gallery = array();
|
||||
|
||||
foreach ( $instance['attachments'] as $attachment ) {
|
||||
$attachment_image_src = wp_get_attachment_image_src( $attachment->ID, 'full' );
|
||||
$attachment_image_src = Image_CDN_Core::cdn_url( $attachment_image_src[0], array( 'w' => $this->instance_width ) ); /** [url, width, height] */
|
||||
|
||||
$caption = wptexturize( wp_strip_all_tags( $attachment->post_excerpt ) );
|
||||
|
||||
$gallery[] = (object) array(
|
||||
'src' => (string) esc_url_raw( $attachment_image_src ),
|
||||
'id' => (string) $attachment->ID,
|
||||
'caption' => (string) $caption,
|
||||
);
|
||||
}
|
||||
|
||||
$max_width = (int) get_option( 'large_size_w' );
|
||||
$max_height = 175;
|
||||
|
||||
if ( (int) $content_width > 0 ) {
|
||||
$max_width = min( (int) $content_width, $max_width );
|
||||
}
|
||||
|
||||
$color = Jetpack_Options::get_option( 'slideshow_background_color', 'black' );
|
||||
$js_attr = array(
|
||||
'gallery' => $gallery,
|
||||
'selector' => $gallery_instance,
|
||||
'width' => $max_width,
|
||||
'height' => $max_height,
|
||||
'trans' => 'fade',
|
||||
'color' => $color,
|
||||
'autostart' => true,
|
||||
);
|
||||
|
||||
$html = $slideshow->slideshow_js( $js_attr );
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to adjust the content width of Jetpack_Tiled_Gallery's in sidebars
|
||||
*
|
||||
* $this->instance_width is filtered in widget() and this filter is added then removed in widget()
|
||||
*
|
||||
* @return int The filtered width
|
||||
*/
|
||||
public function tiled_gallery_content_width() {
|
||||
return $this->instance_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget settings form.
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
* @html-template-var array<string,array<string|int,string|int>> $allowed_values
|
||||
*
|
||||
* @param array $instance Current settings.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$defaults = $this->defaults();
|
||||
$allowed_values = $this->allowed_values(); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Used in included form template.
|
||||
|
||||
$instance = wp_parse_args( (array) $instance, $defaults );
|
||||
|
||||
include __DIR__ . '/gallery/templates/form.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the widget options.
|
||||
*
|
||||
* @param array $new_instance The new instance options.
|
||||
* @param array $old_instance The old instance options.
|
||||
* @return array The saved options.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = $this->sanitize( $new_instance );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the $instance's values to the set of allowed values. If a value is not acceptable,
|
||||
* it is set to its default.
|
||||
*
|
||||
* Helps keep things nice and secure by listing only allowed values.
|
||||
*
|
||||
* @param array $instance The Widget instance to sanitize values for.
|
||||
* @return array $instance The Widget instance with values sanitized
|
||||
*/
|
||||
public function sanitize( $instance ) {
|
||||
$allowed_values = $this->allowed_values();
|
||||
$defaults = $this->defaults();
|
||||
|
||||
foreach ( $instance as $key => $value ) {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
// $instance may hold an Array value type for the Jetpack widget visibility feature.
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = trim( $value );
|
||||
|
||||
if ( isset( $allowed_values[ $key ] ) && $allowed_values[ $key ] && ! array_key_exists( $value, $allowed_values[ $key ] ) ) {
|
||||
$instance[ $key ] = $defaults[ $key ];
|
||||
} else {
|
||||
$instance[ $key ] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a multi-dimensional array of allowed values (and their labels) for all widget form
|
||||
* elements
|
||||
*
|
||||
* To allow all values on an input, omit it from the returned array
|
||||
*
|
||||
* @return array Array of allowed values for each option
|
||||
*/
|
||||
public function allowed_values() {
|
||||
$max_columns = 5;
|
||||
|
||||
// Create an associative array of allowed column values. This just automates the generation of
|
||||
// column <option>s, from 1 to $max_columns.
|
||||
$allowed_columns = array_combine( range( 1, $max_columns ), range( 1, $max_columns ) );
|
||||
|
||||
return array(
|
||||
'type' => array(
|
||||
'rectangular' => __( 'Tiles', 'jetpack' ),
|
||||
'square' => __( 'Square Tiles', 'jetpack' ),
|
||||
'circle' => __( 'Circles', 'jetpack' ),
|
||||
'slideshow' => __( 'Slideshow', 'jetpack' ),
|
||||
),
|
||||
'columns' => $allowed_columns,
|
||||
'link' => array(
|
||||
'carousel' => __( 'Carousel', 'jetpack' ),
|
||||
'post' => __( 'Attachment Page', 'jetpack' ),
|
||||
'file' => __( 'Media File', 'jetpack' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values
|
||||
*
|
||||
* These values are used in new widgets as well as when sanitizing input. If a given value is not allowed,
|
||||
* as defined in allowed_values(), that input is set to the default value defined here.
|
||||
*
|
||||
* @return array Array of default values for the Widget's options
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'title' => '',
|
||||
'type' => 'rectangular',
|
||||
'ids' => '',
|
||||
'columns' => 3,
|
||||
'link' => 'carousel',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend scripts.
|
||||
*/
|
||||
public function enqueue_frontend_scripts() {
|
||||
wp_register_script(
|
||||
'gallery-widget',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/gallery/js/gallery.min.js',
|
||||
'modules/widgets/gallery/js/gallery.js'
|
||||
),
|
||||
array( 'jquery' ),
|
||||
JETPACK__VERSION,
|
||||
false
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'gallery-widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles.
|
||||
*/
|
||||
public function enqueue_admin_scripts() {
|
||||
global $pagenow;
|
||||
|
||||
if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
|
||||
wp_enqueue_media();
|
||||
|
||||
wp_enqueue_script(
|
||||
'gallery-widget-admin',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/gallery/js/admin.min.js',
|
||||
'modules/widgets/gallery/js/admin.js'
|
||||
),
|
||||
array(
|
||||
'jquery',
|
||||
'media-models',
|
||||
'media-views',
|
||||
),
|
||||
'20150501',
|
||||
false
|
||||
);
|
||||
|
||||
$js_settings = array(
|
||||
'thumbSize' => self::THUMB_SIZE,
|
||||
);
|
||||
|
||||
wp_localize_script( 'gallery-widget-admin', '_wpGalleryWidgetAdminSettings', $js_settings );
|
||||
wp_enqueue_style(
|
||||
'gallery-widget-admin',
|
||||
plugins_url( '/gallery/css/admin.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
wp_style_add_data( 'gallery-widget-admin', 'rtl', 'replace' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_gallery_widget_init' );
|
||||
|
||||
/**
|
||||
* Jetpack Gallery widget init; the widget is conditionally registered.
|
||||
*/
|
||||
function jetpack_gallery_widget_init() {
|
||||
/**
|
||||
* Allow the Gallery Widget to be enabled even when Core supports the Media Gallery Widget
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param bool false Whether to force-enable the gallery widget
|
||||
*/
|
||||
if (
|
||||
! apply_filters( 'jetpack_force_enable_gallery_widget', false )
|
||||
&& class_exists( 'WP_Widget_Media_Gallery' )
|
||||
&& Jetpack_Options::get_option( 'gallery_widget_migration' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if ( ! method_exists( 'Jetpack', 'is_module_active' ) || Jetpack::is_module_active( 'tiled-gallery' ) ) {
|
||||
register_widget( 'Jetpack_Gallery_Widget' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.gallery-widget-thumbs-wrapper {
|
||||
margin: -5px 0 0.3em 0;
|
||||
}
|
||||
|
||||
.gallery-widget-thumbs img {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
background-color: #fff;
|
||||
margin: 0 0 5px 5px;
|
||||
float: right;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.gallery-widget-thumbs-wrapper{margin:-5px 0 .3em}.gallery-widget-thumbs img{background-color:#fff;border:1px solid #ccc;float:right;margin:0 0 5px 5px;padding:2px}
|
||||
@@ -0,0 +1,11 @@
|
||||
.gallery-widget-thumbs-wrapper {
|
||||
margin: -5px 0 0.3em 0;
|
||||
}
|
||||
|
||||
.gallery-widget-thumbs img {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
background-color: #fff;
|
||||
margin: 0 5px 5px 0;
|
||||
float: left;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.gallery-widget-thumbs-wrapper{margin:-5px 0 .3em}.gallery-widget-thumbs img{background-color:#fff;border:1px solid #ccc;float:left;margin:0 5px 5px 0;padding:2px}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* This file was automatically generated on Mar 22 2013 21:33:14 */
|
||||
|
||||
.gallery-widget-thumbs-wrapper {
|
||||
margin: -5px 0 0.3em 0;
|
||||
}
|
||||
|
||||
.gallery-widget-thumbs img {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
background-color: #fff;
|
||||
margin: 0 0 5px 5px;
|
||||
float: right;
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/* global _wpMediaViewsL10n, _wpGalleryWidgetAdminSettings */
|
||||
|
||||
( function ( $ ) {
|
||||
var $ids;
|
||||
var $thumbs;
|
||||
|
||||
$( function () {
|
||||
$( document.body ).on( 'click', '.gallery-widget-choose-images', function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
var widget_form = $( this ).closest( 'form, .form' );
|
||||
|
||||
$ids = widget_form.find( '.gallery-widget-ids' );
|
||||
$thumbs = widget_form.find( '.gallery-widget-thumbs' );
|
||||
|
||||
var idsString = $ids.val();
|
||||
|
||||
var attachments = getAttachments( idsString );
|
||||
|
||||
var selection = null;
|
||||
var editing = false;
|
||||
|
||||
if ( attachments ) {
|
||||
selection = getSelection( attachments );
|
||||
|
||||
editing = true;
|
||||
}
|
||||
|
||||
var options = {
|
||||
state: 'gallery-edit',
|
||||
title: wp.media.view.l10n.addMedia,
|
||||
multiple: true,
|
||||
editing: editing,
|
||||
selection: selection,
|
||||
};
|
||||
|
||||
var workflow = getWorkflow( options );
|
||||
|
||||
workflow.open();
|
||||
} );
|
||||
|
||||
// Setup an onchange handler to toggle various options when changing style. The different style options
|
||||
// require different form inputs to be presented in the widget; this event will keep the UI in sync
|
||||
// with the selected style
|
||||
$( '.widget-inside' ).on( 'change', '.gallery-widget-style', setupStyleOptions );
|
||||
|
||||
// Setup the Link To options for all forms currently on the page. Does the same as the onChange handler, but
|
||||
// is called once to display the correct form inputs for each widget on the page
|
||||
setupStyleOptions();
|
||||
} );
|
||||
|
||||
var media = wp.media,
|
||||
l10n;
|
||||
|
||||
// Link any localized strings.
|
||||
l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
|
||||
|
||||
/**
|
||||
* wp.media.view.MediaFrame.GalleryWidget
|
||||
*
|
||||
* This behavior can be very nearly had by setting the workflow's state to 'gallery-edit', but
|
||||
* we cannot use the custom WidgetGalleryEdit controller with it (must overide createStates(),
|
||||
* which is necessary to disable the sidebar gallery settings in the media browser)
|
||||
*/
|
||||
media.view.MediaFrame.GalleryWidget = media.view.MediaFrame.Post.extend( {
|
||||
createStates: function () {
|
||||
var options = this.options;
|
||||
|
||||
// `CollectionEdit` and `CollectionAdd` were only introduced in r27214-core,
|
||||
// so they may not be available yet.
|
||||
if ( 'CollectionEdit' in media.controller ) {
|
||||
this.states.add( [
|
||||
new media.controller.CollectionEdit( {
|
||||
type: 'image',
|
||||
collectionType: 'gallery',
|
||||
title: l10n.editGalleryTitle,
|
||||
SettingsView: media.view.Settings.Gallery,
|
||||
library: options.selection,
|
||||
editing: options.editing,
|
||||
menu: 'gallery',
|
||||
} ),
|
||||
new media.controller.CollectionAdd( {
|
||||
type: 'image',
|
||||
collectionType: 'gallery',
|
||||
title: l10n.addToGalleryTitle,
|
||||
} ),
|
||||
] );
|
||||
} else {
|
||||
// If `CollectionEdit` is not available, then use the old approach.
|
||||
|
||||
if ( ! ( 'WidgetGalleryEdit' in media.controller ) ) {
|
||||
// Remove the gallery settings sidebar when editing widgets.
|
||||
media.controller.WidgetGalleryEdit = media.controller.GalleryEdit.extend( {
|
||||
gallerySettings: function ( /*browser*/ ) {},
|
||||
} );
|
||||
}
|
||||
|
||||
this.states.add( [
|
||||
new media.controller.WidgetGalleryEdit( {
|
||||
library: options.selection,
|
||||
editing: options.editing,
|
||||
menu: 'gallery',
|
||||
} ),
|
||||
new media.controller.GalleryAdd( {} ),
|
||||
] );
|
||||
}
|
||||
},
|
||||
} );
|
||||
|
||||
function setupStyleOptions() {
|
||||
$( '.widget-inside .gallery-widget-style' ).each( function ( /*i*/ ) {
|
||||
var style = $( this ).val();
|
||||
|
||||
var form = $( this ).parents( 'form' );
|
||||
|
||||
switch ( style ) {
|
||||
case 'slideshow':
|
||||
form.find( '.gallery-widget-link-wrapper' ).hide();
|
||||
form.find( '.gallery-widget-columns-wrapper' ).hide();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
form.find( '.gallery-widget-link-wrapper' ).show();
|
||||
form.find( '.gallery-widget-columns-wrapper' ).show();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a given Selection of attachments and a thumbs wrapper div (jQuery object)
|
||||
* and fill it with thumbnails
|
||||
*/
|
||||
function setupThumbs( selection, wrapper ) {
|
||||
wrapper.empty();
|
||||
|
||||
var imageSize = _wpGalleryWidgetAdminSettings.thumbSize;
|
||||
|
||||
selection.each( function ( model ) {
|
||||
var sizedUrl = model.get( 'url' ) + '?w=' + imageSize + '&h=' + imageSize + '&crop=true';
|
||||
|
||||
var thumb = jQuery( '<img>', {
|
||||
src: sizedUrl,
|
||||
alt: model.get( 'title' ),
|
||||
title: model.get( 'title' ),
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
class: 'thumb',
|
||||
} );
|
||||
|
||||
wrapper.append( thumb );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a csv string of ids (as stored in db) and fetch a full Attachments collection
|
||||
*/
|
||||
function getAttachments( idsString ) {
|
||||
if ( ! idsString ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Found in /wp-includes/js/media-editor.js
|
||||
var shortcode = wp.shortcode.next( 'gallery', '[gallery ids="' + idsString + '"]' );
|
||||
|
||||
// Ignore the rest of the match object, to give attachments() below what it expects
|
||||
shortcode = shortcode.shortcode;
|
||||
|
||||
var attachments = wp.media.gallery.attachments( shortcode );
|
||||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an Attachments collection and return a corresponding Selection model that can be
|
||||
* passed to a MediaFrame to prepopulate the gallery picker
|
||||
*/
|
||||
function getSelection( attachments ) {
|
||||
var selection = new wp.media.model.Selection( attachments.models, {
|
||||
props: attachments.props.toJSON(),
|
||||
multiple: true,
|
||||
} );
|
||||
|
||||
selection.gallery = attachments.gallery;
|
||||
|
||||
// Fetch the query's attachments, and then break ties from the
|
||||
// query to allow for sorting.
|
||||
selection.more().done( function () {
|
||||
// Break ties with the query.
|
||||
selection.props.set( { query: false } );
|
||||
selection.unmirror();
|
||||
selection.props.unset( 'orderby' );
|
||||
} );
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a media 'workflow' (MediaFrame). This is the main entry point for the media picker
|
||||
*/
|
||||
function getWorkflow( options ) {
|
||||
var workflow = new wp.media.view.MediaFrame.GalleryWidget( options );
|
||||
|
||||
workflow.on(
|
||||
'update',
|
||||
function ( selection ) {
|
||||
var state = workflow.state();
|
||||
|
||||
selection = selection || state.get( 'selection' );
|
||||
|
||||
if ( ! selection ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the Models down into a simple array of ids that can be easily imploded to a csv string
|
||||
var ids = selection.map( function ( model ) {
|
||||
return model.get( 'id' );
|
||||
} );
|
||||
|
||||
var id_string = ids.join( ',' );
|
||||
|
||||
$ids.val( id_string ).trigger( 'change' );
|
||||
|
||||
setupThumbs( selection, $thumbs );
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
workflow.setState( workflow.options.state );
|
||||
|
||||
return workflow;
|
||||
}
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,14 @@
|
||||
( function ( $ ) {
|
||||
// Fixes a bug with carousels being triggered even when a widget's Link To option is not set to carousel.
|
||||
// Happens when another gallery is loaded on the page, either in a post or separate widget
|
||||
$( 'body' ).on(
|
||||
'click',
|
||||
'.widget-gallery .no-carousel .tiled-gallery-item a',
|
||||
function ( event ) {
|
||||
// Have to trigger default, instead of carousel
|
||||
event.stopPropagation();
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Jetpack_Gallery_Widget backend settings form output.
|
||||
*
|
||||
* @html-template Jetpack_Gallery_Widget::form
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Images:', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<div class="gallery-widget-thumbs-wrapper">
|
||||
<div class="gallery-widget-thumbs">
|
||||
<?php
|
||||
|
||||
// Add the thumbnails to the widget box.
|
||||
$attachments = $this->get_attachments( $instance );
|
||||
|
||||
foreach ( $attachments as $attachment ) {
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'w' => self::THUMB_SIZE,
|
||||
'h' => self::THUMB_SIZE,
|
||||
'crop' => 'true',
|
||||
),
|
||||
wp_get_attachment_url( $attachment->ID )
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
<img src="<?php echo esc_url( $url ); ?>" title="<?php echo esc_attr( $attachment->post_title ); ?>" alt="<?php echo esc_attr( $attachment->post_title ); ?>"
|
||||
width="<?php echo (int) self::THUMB_SIZE; // @phan-suppress-current-line PhanRedundantCondition -- phpcs wants an explicit cast, phan complains it's redundant. 🤷 ?>" height="<?php echo (int) self::THUMB_SIZE; ?>" class="thumb" />
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<a class="button gallery-widget-choose-images"><span class="wp-media-buttons-icon"></span> <?php esc_html_e( 'Choose Images', 'jetpack' ); ?></a>
|
||||
</p>
|
||||
|
||||
<p class="gallery-widget-link-wrapper">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'link' ) ); ?>"><?php esc_html_e( 'Link To:', 'jetpack' ); ?></label>
|
||||
<select name="<?php echo esc_attr( $this->get_field_name( 'link' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'link' ) ); ?>" class="widefat">
|
||||
<?php foreach ( $allowed_values['link'] as $key => $label ) : ?>
|
||||
<option value="<?php echo esc_attr( $key ); ?>"<?php selected( $instance['link'], $key ); ?>><?php echo esc_html( $label ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'random' ) ); ?>"><?php esc_html_e( 'Random Order:', 'jetpack' ); ?></label>
|
||||
<input name="<?php echo esc_attr( $this->get_field_name( 'random' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'random' ) ); ?>" type="checkbox"<?php checked( ! empty( $instance['random'] ) ); ?>>
|
||||
</p>
|
||||
|
||||
<p class="gallery-widget-style-wrapper">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'type' ) ); ?>"><?php esc_html_e( 'Style:', 'jetpack' ); ?></label>
|
||||
<select name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'type' ) ); ?>" class="widefat gallery-widget-style">
|
||||
<?php foreach ( $allowed_values['type'] as $key => $label ) : ?>
|
||||
<option value="<?php echo esc_attr( $key ); ?>"<?php selected( $instance['type'], $key ); ?>><?php echo esc_html( $label ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
|
||||
<?php // Hidden input to hold the selected image ids as a csv list. ?>
|
||||
<input type="hidden" class="gallery-widget-ids" name="<?php echo esc_attr( $this->get_field_name( 'ids' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'ids' ) ); ?>" value="<?php echo esc_attr( $instance['ids'] ); ?>" />
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_goodreads_widget_init' );
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets
|
||||
*/
|
||||
function jetpack_goodreads_widget_init() {
|
||||
register_widget( 'WPCOM_Widget_Goodreads' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Goodreads widget class
|
||||
* Display a user's Goodreads shelf.
|
||||
* Customize user_id, title, and shelf
|
||||
*/
|
||||
class WPCOM_Widget_Goodreads extends WP_Widget {
|
||||
/**
|
||||
* Widget ID based on Goodreads user ID and shelf.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $goodreads_widget_id = 0;
|
||||
|
||||
/**
|
||||
* WPCOM_Widget_Goodreads constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'wpcom-goodreads',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Goodreads', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_goodreads',
|
||||
'description' => __( 'Display your books from Goodreads', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
)
|
||||
);
|
||||
// For user input sanitization and display.
|
||||
$this->shelves = array(
|
||||
'read' => _x( 'Read', 'past participle: books I have read', 'jetpack' ),
|
||||
'currently-reading' => __( 'Currently Reading', 'jetpack' ),
|
||||
'to-read' => _x( 'To Read', 'my list of books to read', 'jetpack' ),
|
||||
);
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Goodreads" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'wpcom-goodreads';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue widget styles.
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_enqueue_style(
|
||||
'goodreads-widget',
|
||||
plugins_url( 'goodreads/css/goodreads.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
wp_style_add_data( 'goodreads-widget', 'rtl', 'replace' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the widget.
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'goodreads' );
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', isset( $instance['title'] ) ? $instance['title'] : '' );
|
||||
|
||||
if ( empty( $instance['user_id'] ) || 'invalid' === $instance['user_id'] ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p>' . sprintf(
|
||||
wp_kses(
|
||||
/* translators: %1$s: link to the widget settings page. %2$s: support article URL for Goodreads widget. */
|
||||
__( 'You need to enter your numeric user ID for the <a href="%1$s">Goodreads Widget</a> to work correctly. <a href="%2$s" target="_blank">Full instructions</a>.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( admin_url( 'widgets.php' ) ),
|
||||
'https://wordpress.com/support/widgets/goodreads-widget/#set-up-the-widget'
|
||||
) . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( $instance['shelf'], $this->shelves ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
$instance['user_id'] = absint( $instance['user_id'] );
|
||||
|
||||
// Set widget ID based on shelf.
|
||||
$this->goodreads_widget_id = $instance['user_id'] . '_' . $instance['shelf'];
|
||||
$this->goodreads_widget_id = str_replace( '-', '_', $this->goodreads_widget_id ); // Goodreads' custom widget does not like dashes.
|
||||
|
||||
if ( empty( $title ) ) {
|
||||
$title = esc_html__( 'Goodreads', 'jetpack' );
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
$goodreads_url = 'https://www.goodreads.com/review/custom_widget/' . rawurlencode( $instance['user_id'] ) . '.' . rawurlencode( $instance['title'] ) . ':%20' . rawurlencode( $instance['shelf'] ) . '?cover_position=&cover_size=small&num_books=5&order=d&shelf=' . rawurlencode( $instance['shelf'] ) . '&sort=date_added&widget_bg_transparent=&widget_id=' . rawurlencode( $this->goodreads_widget_id );
|
||||
|
||||
echo '<div class="jetpack-goodreads-legacy-widget gr_custom_widget" id="gr_custom_widget_' . esc_attr( $this->goodreads_widget_id ) . '"></div>' . "\n";
|
||||
echo '<script src="' . esc_url( $goodreads_url ) . '"></script>' . "\n"; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given Goodreads user ID exists.
|
||||
*
|
||||
* @param string $user_id User ID.
|
||||
*/
|
||||
public function goodreads_user_id_exists( $user_id ) {
|
||||
$url = "https://www.goodreads.com/user/show/$user_id/";
|
||||
$response = wp_remote_head(
|
||||
$url,
|
||||
array(
|
||||
'httpversion' => '1.1',
|
||||
'timeout' => 10,
|
||||
'redirection' => 2,
|
||||
)
|
||||
);
|
||||
if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update widget.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance New widget instance data.
|
||||
* @param array $old_instance Old widget instance data.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = $old_instance;
|
||||
|
||||
$instance['user_id'] = trim( wp_kses( stripslashes( $new_instance['user_id'] ), array() ) );
|
||||
if ( ! empty( $instance['user_id'] ) && ( ! isset( $old_instance['user_id'] ) || $instance['user_id'] !== $old_instance['user_id'] ) ) {
|
||||
if ( ! $this->goodreads_user_id_exists( $instance['user_id'] ) ) {
|
||||
$instance['user_id'] = 'invalid';
|
||||
}
|
||||
}
|
||||
$instance['title'] = wp_kses( stripslashes( $new_instance['title'] ), array() );
|
||||
$shelf = wp_kses( stripslashes( $new_instance['shelf'] ), array() );
|
||||
if ( array_key_exists( $shelf, $this->shelves ) ) {
|
||||
$instance['shelf'] = $shelf;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget settings form.
|
||||
*
|
||||
* @param array $instance Current settings.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
// Defaults.
|
||||
$instance = wp_parse_args(
|
||||
(array) $instance,
|
||||
array(
|
||||
'user_id' => '',
|
||||
'title' => 'Goodreads',
|
||||
'shelf' => 'read',
|
||||
)
|
||||
);
|
||||
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'title' ) ) . '">' . esc_html__( 'Title:', 'jetpack' ) . '
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'title' ) ) . '" type="text" value="' . esc_attr( $instance['title'] ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'user_id' ) ) . '">';
|
||||
printf(
|
||||
wp_kses(
|
||||
/* translators: %s: support article URL for Goodreads widget. */
|
||||
__( 'Goodreads numeric user ID <a href="%s" target="_blank">(instructions)</a>:', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
'https://wordpress.com/support/widgets/goodreads-widget/#set-up-the-widget'
|
||||
);
|
||||
if ( 'invalid' === $instance['user_id'] ) {
|
||||
printf( '<br /><small class="error">%s</small> ', esc_html( __( 'Invalid User ID, please verify and re-enter your Goodreads numeric user ID.', 'jetpack' ) ) );
|
||||
$instance['user_id'] = '';
|
||||
}
|
||||
echo '<input class="widefat" id="' . esc_attr( $this->get_field_id( 'user_id' ) ) . '" name="' . esc_attr( $this->get_field_name( 'user_id' ) ) . '" type="text" value="' . esc_attr( $instance['user_id'] ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'shelf' ) ) . '">' . esc_html__( 'Shelf:', 'jetpack' ) . '
|
||||
<select class="widefat" id="' . esc_attr( $this->get_field_id( 'shelf' ) ) . '" name="' . esc_attr( $this->get_field_name( 'shelf' ) ) . '" >';
|
||||
foreach ( $this->shelves as $_shelf_value => $_shelf_display ) {
|
||||
echo "\t<option value='" . esc_attr( $_shelf_value ) . "'" . selected( $_shelf_value, $instance['shelf'], false ) . '>' . $_shelf_display . "</option>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '</select>
|
||||
</label></p>
|
||||
';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_container"] {
|
||||
/* customize your Goodreads widget container here*/
|
||||
border: 1px solid gray;
|
||||
border-radius:10px;
|
||||
padding: 10px 5px 10px 5px;
|
||||
background-color: #FFF;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_container"] a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jetpack-goodreads-legacy-widget h2[class^="gr_custom_header"] {
|
||||
/* customize your Goodreads header here*/
|
||||
display: none;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_each_container"] {
|
||||
/* customize each individual book container here */
|
||||
width: 100%;
|
||||
clear: both;
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #a7aaad;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_book_container"] {
|
||||
/* customize your book covers here */
|
||||
float: right;
|
||||
overflow: hidden;
|
||||
height: 60px;
|
||||
margin-left: 4px;
|
||||
width: 39px;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_author"] {
|
||||
/* customize your author names here */
|
||||
font-size: 10px;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_tags"] {
|
||||
/* customize your tags here */
|
||||
font-size: 10px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_rating"] {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* This file was automatically generated on Nov 19 2013 15:54:57 */
|
||||
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_container"] {
|
||||
/* customize your Goodreads widget container here*/
|
||||
border: 1px solid gray;
|
||||
border-radius:10px;
|
||||
padding: 10px 5px 10px 5px;
|
||||
background-color: #FFF;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_container"] a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jetpack-goodreads-legacy-widget h2[class^="gr_custom_header"] {
|
||||
/* customize your Goodreads header here*/
|
||||
display: none;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_each_container"] {
|
||||
/* customize each individual book container here */
|
||||
width: 100%;
|
||||
clear: both;
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_book_container"] {
|
||||
/* customize your book covers here */
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
height: 60px;
|
||||
margin-right: 4px;
|
||||
width: 39px;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_author"] {
|
||||
/* customize your author names here */
|
||||
font-size: 10px;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_tags"] {
|
||||
/* customize your tags here */
|
||||
font-size: 10px;
|
||||
color: gray;
|
||||
}
|
||||
.jetpack-goodreads-legacy-widget div[class^="gr_custom_rating"] {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Plugin Name: Google Translate Widget for WordPress.com
|
||||
* Plugin URI: https://automattic.com
|
||||
* Description: Add a widget for automatic translation
|
||||
* Author: Artur Piszek
|
||||
* Version: 0.1
|
||||
* Author URI: https://automattic.com
|
||||
* Text Domain: jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Jetpack_Google_Translate_Widget main class.
|
||||
*/
|
||||
class Jetpack_Google_Translate_Widget extends WP_Widget {
|
||||
/**
|
||||
* Singleton instance of the widget, not to show more than once.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $instance = null;
|
||||
|
||||
/**
|
||||
* Default widget title.
|
||||
*
|
||||
* @var string $default_title
|
||||
*/
|
||||
public $default_title;
|
||||
|
||||
/**
|
||||
* Register widget with WordPress.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'google_translate_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Google Translate', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'Provide your readers with the option to translate your site into their preferred language.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
|
||||
$this->default_title = esc_html__( 'Translate', 'jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend JS scripts.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_register_script(
|
||||
'google-translate-init',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/google-translate/google-translate.min.js',
|
||||
'modules/widgets/google-translate/google-translate.js'
|
||||
),
|
||||
array(),
|
||||
JETPACK__VERSION,
|
||||
false
|
||||
);
|
||||
wp_register_script(
|
||||
'google-translate',
|
||||
'//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit',
|
||||
array( 'google-translate-init' ),
|
||||
JETPACK__VERSION,
|
||||
false
|
||||
);
|
||||
// Admin bar is also displayed on top of the site which causes google translate bar to hide beneath.
|
||||
// Overwrite position of body.admin-bar
|
||||
// This is a hack to show google translate bar a bit lower.
|
||||
$lower_translate_bar = '
|
||||
.admin-bar {
|
||||
position: inherit !important;
|
||||
top: auto !important;
|
||||
}
|
||||
.admin-bar .goog-te-banner-frame {
|
||||
top: 32px !important
|
||||
}
|
||||
@media screen and (max-width: 782px) {
|
||||
.admin-bar .goog-te-banner-frame {
|
||||
top: 46px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
.admin-bar .goog-te-banner-frame {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
';
|
||||
wp_add_inline_style( 'admin-bar', $lower_translate_bar );
|
||||
wp_add_inline_style( 'wpcom-admin-bar', $lower_translate_bar );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the Widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Display arguments.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
// We never should show more than 1 instance of this.
|
||||
if ( null === self::$instance ) {
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => $this->default_title,
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the layout of the Google Translate Widget.
|
||||
*
|
||||
* 3 different integers are accepted.
|
||||
* 0 for the vertical layout.
|
||||
* 1 for the horizontal layout.
|
||||
* 2 for the dropdown only.
|
||||
*
|
||||
* @see https://translate.google.com/manager/website/
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param string $layout layout of the Google Translate Widget.
|
||||
*/
|
||||
$button_layout = apply_filters( 'jetpack_google_translate_widget_layout', 0 );
|
||||
|
||||
if (
|
||||
! is_int( $button_layout )
|
||||
|| 0 > $button_layout
|
||||
|| 2 < $button_layout
|
||||
) {
|
||||
$button_layout = 0;
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'google-translate-init',
|
||||
'_wp_google_translate_widget',
|
||||
array(
|
||||
'lang' => get_locale(),
|
||||
'layout' => (int) $button_layout,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'google-translate-init' );
|
||||
wp_enqueue_script( 'google-translate' );
|
||||
|
||||
$title = $instance['title'];
|
||||
|
||||
if ( ! isset( $title ) ) {
|
||||
$title = $this->default_title;
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $title );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '<div id="google_translate_element"></div>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
self::$instance = $instance;
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'google-translate' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget form in the dashboard.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$title = isset( $instance['title'] ) ? $instance['title'] : false;
|
||||
if ( false === $title ) {
|
||||
$title = $this->default_title;
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array $instance Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
if ( $instance['title'] === $this->default_title ) {
|
||||
$instance['title'] = false; // Store as false in case of language change.
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets.
|
||||
*/
|
||||
function jetpack_google_translate_widget_init() {
|
||||
register_widget( 'Jetpack_Google_Translate_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_google_translate_widget_init' );
|
||||
@@ -0,0 +1,33 @@
|
||||
/* global google, _wp_google_translate_widget */
|
||||
function googleTranslateElementInit() {
|
||||
var lang = 'en';
|
||||
var langParam;
|
||||
var langRegex = /[?&#]lang=([a-zA-Z\-_]+)/;
|
||||
if (
|
||||
typeof _wp_google_translate_widget === 'object' &&
|
||||
typeof _wp_google_translate_widget.lang === 'string'
|
||||
) {
|
||||
lang = _wp_google_translate_widget.lang;
|
||||
}
|
||||
langParam = window.location.href.match( langRegex );
|
||||
if ( langParam ) {
|
||||
window.location.href =
|
||||
window.location.href.replace( langRegex, '' ).replace( /#googtrans\([a-zA-Z\-_|]+\)/, '' ) +
|
||||
'#googtrans(' +
|
||||
lang +
|
||||
'|' +
|
||||
langParam[ 1 ] +
|
||||
')';
|
||||
}
|
||||
new google.translate.TranslateElement(
|
||||
{
|
||||
pageLanguage: lang,
|
||||
layout: _wp_google_translate_widget.layout,
|
||||
autoDisplay: false,
|
||||
},
|
||||
'google_translate_element'
|
||||
);
|
||||
}
|
||||
if ( module ) {
|
||||
module.exports = googleTranslateElementInit;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.widget-grofile {
|
||||
}
|
||||
.widget-grofile h4 {
|
||||
margin: 1em 0 .5em;
|
||||
}
|
||||
.widget-grofile ul.grofile-urls {
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.widget-grofile ul.grofile-accounts li {
|
||||
list-style: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.widget-grofile ul.grofile-accounts li::before {
|
||||
content: "" !important; /* Kubrick :( */
|
||||
}
|
||||
.widget-grofile .grofile-accounts-logo {
|
||||
background-image: url('https://secure.gravatar.com/images/grav-share-sprite.png');
|
||||
background-repeat: no-repeat;
|
||||
/*background-position: -16px -16px;*/
|
||||
width: 16px; /* So we don't show the topmost logo */
|
||||
height: 16px; /* So we don't show the topmost logo */
|
||||
float: left;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.rtl .widget-grofile .grofile-accounts-logo {
|
||||
margin-left: 8px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.grofile-thumbnail {
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
}
|
||||
@media
|
||||
only screen and (-webkit-min-device-pixel-ratio: 1.5),
|
||||
only screen and (-o-min-device-pixel-ratio: 3/2),
|
||||
only screen and (min--moz-device-pixel-ratio: 1.5),
|
||||
only screen and (min-device-pixel-ratio: 1.5) {
|
||||
.widget-grofile .grofile-accounts-logo {
|
||||
background-image: url('https://secure.gravatar.com/images/grav-share-sprite-2x.png');
|
||||
background-size: 16px 784px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_gravatar_profile_widget_init' );
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets
|
||||
*/
|
||||
function jetpack_gravatar_profile_widget_init() {
|
||||
register_widget( 'Jetpack_Gravatar_Profile_Widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a widgetized version of your Gravatar Profile
|
||||
* https://blog.gravatar.com/2010/03/26/gravatar-profiles/
|
||||
*/
|
||||
class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
|
||||
/**
|
||||
* Jetpack_Gravatar_Profile_Widget constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'grofile',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Gravatar Profile', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget-grofile grofile',
|
||||
'description' => __( 'Display a mini version of your Gravatar Profile', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_action( 'admin_footer-widgets.php', array( $this, 'admin_script' ) );
|
||||
}
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the Widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Display arguments.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
/**
|
||||
* Fires when an item is displayed on the front end.
|
||||
*
|
||||
* Can be used to track stats about the number of displays for a specific item
|
||||
*
|
||||
* @module widgets, shortcodes
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @param string widget_view Item type (e.g. widget, or embed).
|
||||
* @param string grofile Item description (e.g. grofile, goodreads).
|
||||
*/
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'grofile' );
|
||||
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => '',
|
||||
'email' => '',
|
||||
)
|
||||
);
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
|
||||
if ( ! $instance['email'] ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '<p>' . wp_kses(
|
||||
sprintf(
|
||||
/* translators: %s is a link to the widget settings page. */
|
||||
__( 'You need to select what to show in this <a href="%s">Gravatar Profile widget</a>.', 'jetpack' ),
|
||||
admin_url( 'widgets.php' )
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
),
|
||||
)
|
||||
) . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$profile = $this->get_profile( $instance['email'] );
|
||||
|
||||
if ( ! empty( $profile ) ) {
|
||||
$profile = wp_parse_args(
|
||||
$profile,
|
||||
array(
|
||||
'thumbnailUrl' => '',
|
||||
'profileUrl' => '',
|
||||
'displayName' => '',
|
||||
'aboutMe' => '',
|
||||
'urls' => array(),
|
||||
'accounts' => array(),
|
||||
)
|
||||
);
|
||||
$base_width = 320;
|
||||
$gravatar_url = add_query_arg( 's', $base_width, $profile['thumbnailUrl'] ); // The default grav returned by grofiles is super small.
|
||||
|
||||
// Generate a srcset with larger sizes for high DPI screens.
|
||||
$srcset = '';
|
||||
$multipliers = array( 1, 1.5, 2, 3, 4 );
|
||||
$srcset_values = array();
|
||||
foreach ( $multipliers as $multiplier ) {
|
||||
$srcset_width = (int) ( $base_width * $multiplier );
|
||||
$srcset_url = add_query_arg( 's', $srcset_width, $profile['thumbnailUrl'] );
|
||||
$srcset_values[] = "{$srcset_url} {$multiplier}x";
|
||||
}
|
||||
$srcset = implode( ', ', $srcset_values );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_scripts();
|
||||
|
||||
?>
|
||||
<img
|
||||
src="<?php echo esc_url( $gravatar_url ); ?>"
|
||||
srcset="<?php echo esc_attr( $srcset ); ?>"
|
||||
class="grofile-thumbnail no-grav"
|
||||
alt="<?php echo esc_attr( $profile['displayName'] ); ?>"
|
||||
loading="lazy" />
|
||||
<div class="grofile-meta">
|
||||
<h4><a href="<?php echo esc_url( $profile['profileUrl'] ); ?>"><?php echo esc_html( $profile['displayName'] ); ?></a></h4>
|
||||
<p><?php echo wp_kses_post( $profile['aboutMe'] ); ?></p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
if ( $instance['show_personal_links'] ) {
|
||||
$this->display_personal_links( (array) $profile['urls'] );
|
||||
}
|
||||
|
||||
if ( $instance['show_account_links'] ) {
|
||||
$this->display_accounts( (array) $profile['accounts'] );
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<p><a href="<?php echo esc_url( $profile['profileUrl'] ); ?>" class="grofile-full-link">
|
||||
<?php
|
||||
echo esc_html(
|
||||
/**
|
||||
* Filter the Gravatar Profile widget's profile link title.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $str Profile link title.
|
||||
*/
|
||||
apply_filters(
|
||||
'jetpack_gravatar_full_profile_title',
|
||||
__( 'View Full Profile →', 'jetpack' )
|
||||
)
|
||||
);
|
||||
?>
|
||||
</a></p>
|
||||
|
||||
<?php
|
||||
} elseif ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo '<p>' . esc_html__( 'Error loading profile', 'jetpack' ) . '</p>';
|
||||
}
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the "Personal Links" section.
|
||||
*
|
||||
* @param array $personal_links Array of links.
|
||||
*/
|
||||
public function display_personal_links( $personal_links = array() ) {
|
||||
if ( empty( $personal_links ) ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<h4>
|
||||
<?php
|
||||
echo esc_html(
|
||||
apply_filters(
|
||||
/**
|
||||
* Filter the Gravatar Profile widget's "Personal Links" section title.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $str "Personal Links" section title.
|
||||
*/
|
||||
'jetpack_gravatar_personal_links_title',
|
||||
__( 'Personal Links', 'jetpack' )
|
||||
)
|
||||
);
|
||||
?>
|
||||
</h4>
|
||||
<ul class="grofile-urls grofile-links">
|
||||
|
||||
<?php foreach ( $personal_links as $personal_link ) : ?>
|
||||
<?php if ( is_array( $personal_link ) ) : ?>
|
||||
<li>
|
||||
<a href="<?php echo esc_url( $personal_link['value'] ); ?>">
|
||||
<?php
|
||||
$link_title = ( ! empty( $personal_link['title'] ) ) ? $personal_link['title'] : $personal_link['value'];
|
||||
echo esc_html( $link_title );
|
||||
?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the "Verified Services" accounts.
|
||||
*
|
||||
* @param array $accounts Array of social accounts.
|
||||
*/
|
||||
public function display_accounts( $accounts = array() ) {
|
||||
if ( empty( $accounts ) ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<h4>
|
||||
<?php
|
||||
echo esc_html(
|
||||
/**
|
||||
* Filter the Gravatar Profile widget's "Verified Services" section title.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $str "Verified Services" section title.
|
||||
*/
|
||||
apply_filters(
|
||||
'jetpack_gravatar_verified_services_title',
|
||||
__( 'Verified Services', 'jetpack' )
|
||||
)
|
||||
);
|
||||
?>
|
||||
</h4>
|
||||
<ul class="grofile-urls grofile-accounts">
|
||||
|
||||
<?php
|
||||
foreach ( $accounts as $account ) :
|
||||
if ( 'true' !== $account['verified'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sanitized_service_name = $this->get_sanitized_service_name( $account['shortname'] );
|
||||
$link_title = sprintf(
|
||||
/* translators: %1$s: service username. %2$s: service name ( Facebook, Twitter, etc.) */
|
||||
_x( '%1$s on %2$s', '1: User Name, 2: Service Name (Facebook, Twitter, ...)', 'jetpack' ),
|
||||
esc_html( $account['display'] ),
|
||||
esc_html( $sanitized_service_name )
|
||||
);
|
||||
?>
|
||||
|
||||
<li>
|
||||
<a href="<?php echo esc_url( $account['url'] ); ?>" title="<?php echo esc_html( $link_title ); ?>">
|
||||
<span class="grofile-accounts-logo grofile-accounts-<?php echo esc_attr( $account['shortname'] ); ?> accounts_<?php echo esc_attr( $account['shortname'] ); ?>"></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue CSS and JavaScript.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_style(
|
||||
'gravatar-profile-widget',
|
||||
plugins_url( 'gravatar-profile.css', __FILE__ ),
|
||||
array(),
|
||||
'20120711'
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'gravatar-card-services',
|
||||
'https://secure.gravatar.com/css/services.css',
|
||||
array(),
|
||||
defined( 'GROFILES__CACHE_BUSTER' ) ? GROFILES__CACHE_BUSTER : gmdate( 'YW' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the widget settings form.
|
||||
*
|
||||
* @param array $instance Current settings.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$title = isset( $instance['title'] ) ? $instance['title'] : '';
|
||||
$email = isset( $instance['email'] ) ? $instance['email'] : '';
|
||||
$email_user = isset( $instance['email_user'] ) ? $instance['email_user'] : get_current_user_id();
|
||||
$show_personal_links = isset( $instance['show_personal_links'] ) ? (bool) $instance['show_personal_links'] : '';
|
||||
$show_account_links = isset( $instance['show_account_links'] ) ? (bool) $instance['show_account_links'] : '';
|
||||
$profile_url = 'https://gravatar.com/profile/edit';
|
||||
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$profile_url = admin_url( 'profile.php' );
|
||||
|
||||
if ( isset( $_REQUEST['calypso'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$profile_url = 'https://wordpress.com/me';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
|
||||
<?php esc_html_e( 'Title', 'jetpack' ); ?> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'email_user' ) ); ?>">
|
||||
<?php esc_html_e( 'Select a user or pick "custom" and enter a custom email address.', 'jetpack' ); ?>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
wp_dropdown_users(
|
||||
array(
|
||||
'show_option_none' => __( 'Custom', 'jetpack' ),
|
||||
'selected' => $email_user,
|
||||
'name' => $this->get_field_name( 'email_user' ),
|
||||
'id' => $this->get_field_id( 'email_user' ),
|
||||
'class' => 'gravatar-profile-user-select',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p class="gprofile-email-container <?php echo empty( $email_user ) || -1 === (int) $email_user ? '' : 'hidden'; ?>">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>"><?php esc_html_e( 'Custom Email Address', 'jetpack' ); ?>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" type="text" value="<?php echo esc_attr( $email ); ?>" />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'show_personal_links' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'show_personal_links' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'show_personal_links' ) ); ?>" <?php checked( $show_personal_links ); ?> />
|
||||
<?php esc_html_e( 'Show Personal Links', 'jetpack' ); ?>
|
||||
<br />
|
||||
<small><?php esc_html_e( 'Links to your websites, blogs, or any other sites that help describe who you are.', 'jetpack' ); ?></small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'show_account_links' ) ); ?>">
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'show_account_links' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'show_account_links' ) ); ?>" <?php checked( $show_account_links ); ?> />
|
||||
<?php esc_html_e( 'Show Account Links', 'jetpack' ); ?>
|
||||
<br />
|
||||
<small><?php esc_html_e( 'Links to services that you use across the web.', 'jetpack' ); ?></small>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p><a href="<?php echo esc_url( $profile_url ); ?>" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( 'Edit Your Profile', 'jetpack' ); ?></a> | <a href="https://gravatar.com" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( "What's a Gravatar?", 'jetpack' ); ?></a></p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline admin script.
|
||||
*/
|
||||
public function admin_script() {
|
||||
?>
|
||||
<script>
|
||||
jQuery( function( $ ) {
|
||||
$( '.wrap' ).on( 'change', '.gravatar-profile-user-select', function() {
|
||||
var $input = $(this).closest('.widget-inside').find('.gprofile-email-container');
|
||||
if ( '-1' === this.value.toLowerCase() ) {
|
||||
$input.show();
|
||||
} else {
|
||||
$input.hide();
|
||||
}
|
||||
});
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Update widget.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance New widget instance data.
|
||||
* @param array $old_instance Old widget instance data.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
$instance['title'] = isset( $new_instance['title'] ) ? wp_kses( $new_instance['title'], array() ) : '';
|
||||
$instance['email'] = isset( $new_instance['email'] ) ? wp_kses( $new_instance['email'], array() ) : '';
|
||||
$instance['email_user'] = isset( $new_instance['email_user'] ) ? (int) $new_instance['email_user'] : -1;
|
||||
$instance['show_personal_links'] = isset( $new_instance['show_personal_links'] ) ? (bool) $new_instance['show_personal_links'] : false;
|
||||
$instance['show_account_links'] = isset( $new_instance['show_account_links'] ) ? (bool) $new_instance['show_account_links'] : false;
|
||||
|
||||
if ( $instance['email_user'] > 0 ) {
|
||||
$user = get_userdata( $instance['email_user'] );
|
||||
$instance['email'] = $user->user_email;
|
||||
}
|
||||
|
||||
$hashed_email = md5( strtolower( trim( $instance['email'] ) ) );
|
||||
$cache_key = 'grofile-' . $hashed_email;
|
||||
delete_transient( $cache_key );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Gravatar profile for a given email address.
|
||||
*
|
||||
* @param string $email Email address.
|
||||
*/
|
||||
private function get_profile( $email ) {
|
||||
$hashed_email = md5( strtolower( trim( $email ) ) );
|
||||
$cache_key = 'grofile-' . $hashed_email;
|
||||
$profile = get_transient( $cache_key );
|
||||
|
||||
if ( ! $profile ) {
|
||||
$profile_url = sprintf(
|
||||
'https://secure.gravatar.com/%s.json',
|
||||
$hashed_email
|
||||
);
|
||||
|
||||
$expire = 300;
|
||||
$response = wp_remote_get(
|
||||
esc_url_raw( $profile_url ),
|
||||
array( 'User-Agent' => 'WordPress.com Gravatar Profile Widget' )
|
||||
);
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 === $response_code ) {
|
||||
$profile = wp_remote_retrieve_body( $response );
|
||||
$profile = json_decode( $profile, true );
|
||||
|
||||
if ( is_array( $profile ) && ! empty( $profile['entry'] ) && is_array( $profile['entry'] ) ) {
|
||||
$expire = 900; // Cache for 15 minutes.
|
||||
$profile = $profile['entry'][0];
|
||||
} else {
|
||||
// Something strange happened. Cache for 5 minutes.
|
||||
$profile = array();
|
||||
}
|
||||
} else {
|
||||
$expire = 900; // Cache for 15 minutes.
|
||||
$profile = array();
|
||||
}
|
||||
|
||||
set_transient( $cache_key, $profile, $expire );
|
||||
}
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return properly capitalized service name.
|
||||
*
|
||||
* @param string $shortname The service.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_sanitized_service_name( $shortname ) {
|
||||
// Some services have stylized or mixed cap names *cough* WP *cough*.
|
||||
switch ( $shortname ) {
|
||||
case 'friendfeed':
|
||||
return 'FriendFeed';
|
||||
case 'linkedin':
|
||||
return 'LinkedIn';
|
||||
case 'yahoo':
|
||||
return 'Yahoo!';
|
||||
case 'youtube':
|
||||
return 'YouTube';
|
||||
// phpcs:ignore WordPress.WP.CapitalPDangit
|
||||
case 'wordpress':
|
||||
return 'WordPress';
|
||||
case 'tripit':
|
||||
return 'TripIt';
|
||||
case 'myspace':
|
||||
return 'MySpace';
|
||||
case 'foursquare':
|
||||
return 'foursquare';
|
||||
case 'google':
|
||||
return 'Google+';
|
||||
default:
|
||||
// Others don't.
|
||||
$shortname = ucwords( $shortname );
|
||||
}
|
||||
return $shortname;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Module Name: Image Widget
|
||||
* Module Description: Easily add images to your theme's sidebar.
|
||||
* Sort Order: 20
|
||||
* First Introduced: 1.2
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_image_widget_init', 11 );
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets
|
||||
*/
|
||||
function jetpack_image_widget_init() {
|
||||
if ( class_exists( 'WP_Widget_Media_Image' ) && Jetpack_Options::get_option( 'image_widget_migration' ) ) {
|
||||
return;
|
||||
}
|
||||
register_widget( 'Jetpack_Image_Widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Jetpack_Image_Widget main class.
|
||||
*/
|
||||
class Jetpack_Image_Widget extends WP_Widget {
|
||||
/**
|
||||
* Register widget with WordPress.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'image',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Image', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_image',
|
||||
'description' => __( 'Display an image in your sidebar', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads file for front-end widget style.
|
||||
*
|
||||
* @uses wp_enqueue_style(), plugins_url()
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_enqueue_style( 'jetpack_image_widget', plugins_url( 'image-widget/style.css', __FILE__ ), array(), '20140808' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => '',
|
||||
'img_url' => '',
|
||||
)
|
||||
);
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
if ( $title ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( $instance['img_url'] ) {
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
$output = '<img src="' . esc_url( $instance['img_url'] ) . '" ';
|
||||
|
||||
if ( '' !== (string) $instance['alt_text'] ) {
|
||||
$output .= 'alt="' . esc_attr( $instance['alt_text'] ) . '" ';
|
||||
}
|
||||
if ( '' !== (string) $instance['img_title'] ) {
|
||||
$output .= 'title="' . esc_attr( $instance['img_title'] ) . '" ';
|
||||
}
|
||||
if ( '' !== (string) $instance['caption'] ) {
|
||||
$output .= 'class="align' . esc_attr( $instance['align'] ) . '" ';
|
||||
}
|
||||
if ( '' !== (string) $instance['img_width'] ) {
|
||||
$output .= 'width="' . esc_attr( $instance['img_width'] ) . '" ';
|
||||
}
|
||||
if ( '' !== (string) $instance['img_height'] ) {
|
||||
$output .= 'height="' . esc_attr( $instance['img_height'] ) . '" ';
|
||||
}
|
||||
$output .= '/>';
|
||||
|
||||
$output = apply_filters( 'jetpack_image_cdn_content', $output );
|
||||
|
||||
if ( $instance['link'] ) {
|
||||
$target = ! empty( $instance['link_target_blank'] )
|
||||
? 'target="_blank"'
|
||||
: '';
|
||||
$output = '<a ' . $target . ' href="' . esc_url( $instance['link'] ) . '">' . $output . '</a>';
|
||||
}
|
||||
if ( '' !== (string) $instance['caption'] ) {
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$caption = apply_filters( 'widget_text', $instance['caption'] );
|
||||
$img_width = ( ! empty( $instance['img_width'] ) ? 'style="width: ' . esc_attr( $instance['img_width'] ) . 'px"' : '' );
|
||||
$output = '<figure ' . $img_width . ' class="wp-caption align' . esc_attr( $instance['align'] ) . '">
|
||||
' . $output . '
|
||||
<figcaption class="wp-caption-text">' . $caption . '</figcaption>
|
||||
</figure>'; // wp_kses_post caption on update.
|
||||
}
|
||||
echo '<div class="jetpack-image-container">' . do_shortcode( $output ) . '</div>';
|
||||
} elseif ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo '<p>' . wp_kses(
|
||||
sprintf(
|
||||
/* translators: %s link to the widget settings page. */
|
||||
__( 'Image missing or invalid URL. Please check the Image widget URL in your <a href="%s">widget settings</a>.', 'jetpack' ),
|
||||
admin_url( 'widgets.php' )
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
) . '</p>';
|
||||
}
|
||||
|
||||
echo "\n" . $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'image' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$allowed_caption_html = array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'title' => array(),
|
||||
),
|
||||
'b' => array(),
|
||||
'em' => array(),
|
||||
'i' => array(),
|
||||
'p' => array(),
|
||||
'strong' => array(),
|
||||
);
|
||||
|
||||
$instance = $old_instance;
|
||||
|
||||
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
|
||||
$instance['img_url'] = esc_url( trim( $new_instance['img_url'] ) );
|
||||
$instance['alt_text'] = wp_strip_all_tags( $new_instance['alt_text'] );
|
||||
$instance['img_title'] = wp_strip_all_tags( $new_instance['img_title'] );
|
||||
$instance['caption'] = wp_kses( stripslashes( $new_instance['caption'] ), $allowed_caption_html );
|
||||
$instance['align'] = $new_instance['align'];
|
||||
$instance['link'] = esc_url( trim( $new_instance['link'] ) );
|
||||
$instance['link_target_blank'] = isset( $new_instance['link_target_blank'] ) ? (bool) $new_instance['link_target_blank'] : false;
|
||||
|
||||
$new_img_width = absint( $new_instance['img_width'] );
|
||||
$new_img_height = absint( $new_instance['img_height'] );
|
||||
|
||||
if ( ! empty( $instance['img_url'] ) && 0 === $new_img_width && 0 === $new_img_height ) {
|
||||
// Download the url to a local temp file and then process it with getimagesize so we can optimize browser layout.
|
||||
$tmp_file = download_url( $instance['img_url'], 10 );
|
||||
if ( ! is_wp_error( $tmp_file ) ) {
|
||||
$size = getimagesize( $tmp_file );
|
||||
|
||||
$width = $size[0];
|
||||
$instance['img_width'] = absint( $width );
|
||||
|
||||
$height = $size[1];
|
||||
$instance['img_height'] = absint( $height );
|
||||
|
||||
wp_delete_file( $tmp_file );
|
||||
} else {
|
||||
$instance['img_width'] = $new_img_width;
|
||||
$instance['img_height'] = $new_img_height;
|
||||
}
|
||||
} else {
|
||||
$instance['img_width'] = $new_img_width;
|
||||
$instance['img_height'] = $new_img_height;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
// Defaults.
|
||||
$instance = wp_parse_args(
|
||||
(array) $instance,
|
||||
array(
|
||||
'title' => '',
|
||||
'img_url' => '',
|
||||
'alt_text' => '',
|
||||
'img_title' => '',
|
||||
'caption' => '',
|
||||
'align' => 'none',
|
||||
'img_width' => '',
|
||||
'img_height' => '',
|
||||
'link' => '',
|
||||
'link_target_blank' => false,
|
||||
)
|
||||
);
|
||||
|
||||
$title = esc_attr( $instance['title'] );
|
||||
$img_url = esc_url( $instance['img_url'], null, 'display' );
|
||||
$alt_text = esc_attr( $instance['alt_text'] );
|
||||
$img_title = esc_attr( $instance['img_title'] );
|
||||
$caption = esc_textarea( $instance['caption'] );
|
||||
$align = esc_attr( $instance['align'] );
|
||||
$img_width = esc_attr( $instance['img_width'] );
|
||||
$img_height = esc_attr( $instance['img_height'] );
|
||||
$link_target_blank = checked( $instance['link_target_blank'], true, false );
|
||||
|
||||
$link = esc_url( $instance['link'], null, 'display' );
|
||||
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'title' ) ) . '">' . esc_html__( 'Widget title:', 'jetpack' ) . '
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'title' ) ) . '" type="text" value="' . esc_attr( $title ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'img_url' ) ) . '">' . esc_html__( 'Image URL:', 'jetpack' ) . '
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'img_url' ) ) . '" name="' . esc_attr( $this->get_field_name( 'img_url' ) ) . '" type="text" value="' . esc_attr( $img_url ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'alt_text' ) ) . '">' . esc_html__( 'Alternate text:', 'jetpack' ) . ' <a href="https://support.wordpress.com/widgets/image-widget/#image-widget-alt-text" target="_blank">( ? )</a>
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'alt_text' ) ) . '" name="' . esc_attr( $this->get_field_name( 'alt_text' ) ) . '" type="text" value="' . esc_attr( $alt_text ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'img_title' ) ) . '">' . esc_html__( 'Image title:', 'jetpack' ) . ' <a href="https://support.wordpress.com/widgets/image-widget/#image-widget-title" target="_blank">( ? )</a>
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'img_title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'img_title' ) ) . '" type="text" value="' . esc_attr( $img_title ) . '" />
|
||||
</label></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'caption' ) ) . '">' . esc_html__( 'Caption:', 'jetpack' ) . ' <a href="https://support.wordpress.com/widgets/image-widget/#image-widget-caption" target="_blank">( ? )</a>
|
||||
<textarea class="widefat" id="' . esc_attr( $this->get_field_id( 'caption' ) ) . '" name="' . esc_attr( $this->get_field_name( 'caption' ) ) . '" rows="2" cols="20">' . esc_textarea( $caption ) . '</textarea>
|
||||
</label></p>';
|
||||
|
||||
$alignments = array(
|
||||
'none' => __( 'None', 'jetpack' ),
|
||||
'left' => __( 'Left', 'jetpack' ),
|
||||
'center' => __( 'Center', 'jetpack' ),
|
||||
'right' => __( 'Right', 'jetpack' ),
|
||||
);
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'align' ) ) . '">' . esc_html__( 'Image Alignment:', 'jetpack' ) . '
|
||||
<select id="' . esc_attr( $this->get_field_id( 'align' ) ) . '" name="' . esc_attr( $this->get_field_name( 'align' ) ) . '">';
|
||||
foreach ( $alignments as $alignment => $alignment_name ) {
|
||||
echo '<option value="' . esc_attr( $alignment ) . '" ';
|
||||
if ( $alignment === $align ) {
|
||||
echo 'selected="selected" ';
|
||||
}
|
||||
echo '>' . esc_html( $alignment_name ) . "</option>\n";
|
||||
}
|
||||
echo '</select></label></p>';
|
||||
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'img_width' ) ) . '">' . esc_html__( 'Width in pixels:', 'jetpack' ) . '
|
||||
<input size="3" id="' . esc_attr( $this->get_field_id( 'img_width' ) ) . '" name="' . esc_attr( $this->get_field_name( 'img_width' ) ) . '" type="text" value="' . esc_attr( $img_width ) . '" />
|
||||
</label>
|
||||
<label for="' . esc_attr( $this->get_field_id( 'img_height' ) ) . '">' . esc_html__( 'Height in pixels:', 'jetpack' ) . '
|
||||
<input size="3" id="' . esc_attr( $this->get_field_id( 'img_height' ) ) . '" name="' . esc_attr( $this->get_field_name( 'img_height' ) ) . '" type="text" value="' . esc_attr( $img_height ) . '" />
|
||||
</label><br />
|
||||
<small>' . esc_html__( 'If empty, we will attempt to determine the image size.', 'jetpack' ) . '</small></p>
|
||||
<p><label for="' . esc_attr( $this->get_field_id( 'link' ) ) . '">' . esc_html__( 'Link URL (when the image is clicked):', 'jetpack' ) . '
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'link' ) ) . '" name="' . esc_attr( $this->get_field_name( 'link' ) ) . '" type="text" value="' . esc_attr( $link ) . '" />
|
||||
</label>
|
||||
<label for="' . esc_attr( $this->get_field_id( 'link_target_blank' ) ) . '">
|
||||
<input type="checkbox" name="' . esc_attr( $this->get_field_name( 'link_target_blank' ) ) . '" id="' . esc_attr( $this->get_field_id( 'link_target_blank' ) ) . '" value="1"' . esc_attr( $link_target_blank ) . '/>
|
||||
' . esc_html__( 'Open link in a new window/tab', 'jetpack' ) . '
|
||||
</label></p>';
|
||||
}
|
||||
} // Class Jetpack_Image_Widget
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Image Widget styles for Jetpack
|
||||
*/
|
||||
|
||||
/* Clear floats */
|
||||
.jetpack-image-container:after {
|
||||
clear: both;
|
||||
}
|
||||
.jetpack-image-container:before,
|
||||
.jetpack-image-container:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
.wpcom-instagram-images {
|
||||
display: grid;
|
||||
grid-gap: 0.5rem;
|
||||
grid-auto-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpcom-instagram-images:before,
|
||||
.wpcom-instagram-images:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.wpcom-instagram-images a {
|
||||
display: block;
|
||||
margin: 4px 2px;
|
||||
}
|
||||
.wpcom-instagram-columns-2 a,
|
||||
.wpcom-instagram-columns-3 a {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.wpcom-instagram-columns-2 a {
|
||||
width: calc(50% - 8px);
|
||||
}
|
||||
.wpcom-instagram-columns-3 a {
|
||||
width: calc(33.3333% - 8px);
|
||||
}
|
||||
|
||||
.wpcom-instagram-images .sq-bg-image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.wpcom-instagram-columns-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.wpcom-instagram-columns-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@supports (display:grid) {
|
||||
.wpcom-instagram-images a {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wpcom-instagram-columns-2 a,
|
||||
.wpcom-instagram-columns-3 a {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* Jetpack_Internet_Defense_League_Widget main class.
|
||||
*/
|
||||
class Jetpack_Internet_Defense_League_Widget extends WP_Widget {
|
||||
/**
|
||||
* Default widget settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $defaults = array();
|
||||
|
||||
/**
|
||||
* Selected badge to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $badge;
|
||||
/**
|
||||
* Badge display options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $badges = array();
|
||||
|
||||
/**
|
||||
* Jetpack_Internet_Defense_League_Widget constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'internet_defense_league_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Internet Defense League', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => esc_html__( 'Show your support for the Internet Defense League.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$this->badges = array(
|
||||
'shield_badge' => esc_html__( 'Shield Badge', 'jetpack' ),
|
||||
'super_badge' => esc_html__( 'Super Badge', 'jetpack' ),
|
||||
'side_bar_badge' => esc_html__( 'Red Cat Badge', 'jetpack' ),
|
||||
);
|
||||
|
||||
$this->defaults = array(
|
||||
'badge' => key( $this->badges ),
|
||||
);
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Internet Defense League" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'internet_defense_league_widget';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the Widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Display arguments.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
|
||||
if ( 'none' !== $instance['badge'] ) {
|
||||
if ( ! isset( $this->badges[ $instance['badge'] ] ) ) {
|
||||
$instance['badge'] = $this->defaults['badge'];
|
||||
}
|
||||
$badge_url = 'internet-defense-league/' . $instance['badge'] . '.png';
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
printf(
|
||||
'<p><a href="%1$s" target="_blank" rel="noopener noreferrer"><img src="%2$s" alt="%3$s" style="max-width: 100%%; height: auto;" /></a></p>',
|
||||
esc_url( 'https://www.fightforthefuture.org/' ),
|
||||
esc_url( plugins_url( $badge_url, __FILE__ ) ),
|
||||
esc_attr__( 'Member of The Internet Defense League', 'jetpack' )
|
||||
);
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'internet_defense_league' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline footer script.
|
||||
*
|
||||
* @deprecated 12.5 This widget does not enqueue scripts anymore.
|
||||
*/
|
||||
public function footer_script() {
|
||||
_deprecated_function( __METHOD__, 'jetpack-12.5' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget form in the dashboard.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
|
||||
echo '<p><label>';
|
||||
echo esc_html__( 'Which badge would you like to display?', 'jetpack' ) . '<br />';
|
||||
|
||||
echo '<select class="widefat" name="' . esc_attr( $this->get_field_name( 'badge' ) ) . '">';
|
||||
foreach ( $this->badges as $option_slug => $option_name ) {
|
||||
echo '<option value="' . esc_attr( $option_slug ) . '"' . selected( $option_slug, $instance['badge'], false ) . '>' . esc_html( $option_name ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</label></p>';
|
||||
|
||||
echo '<p>' . wp_kses(
|
||||
sprintf(
|
||||
/* translators: %s is an HTML link to the website of an internet campaign called the "Internet Defense League" */
|
||||
_x( 'Learn more about the %s', 'the Internet Defense League', 'jetpack' ),
|
||||
'<a href="https://www.fightforthefuture.org/" target="_blank" rel="noopener noreferrer">Internet Defense League</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'rel' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a select form field.
|
||||
*
|
||||
* @deprecated 12.5 This widget only has one option now, no need for an extracted method.
|
||||
*
|
||||
* @param string $field_name Name of the field.
|
||||
* @param array $options Array of options.
|
||||
* @param string $default Default option.
|
||||
*/
|
||||
public function select( $field_name, $options, $default = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
_deprecated_function( __METHOD__, 'jetpack-12.5' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update widget.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance New widget instance data.
|
||||
* @param array $old_instance Old widget instance data.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
$instance['badge'] = ( isset( $new_instance['badge'] ) && isset( $this->badges[ $new_instance['badge'] ] ) )
|
||||
? $new_instance['badge']
|
||||
: $this->defaults['badge'];
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the widget.
|
||||
*/
|
||||
function jetpack_internet_defense_league_init() {
|
||||
register_widget( 'Jetpack_Internet_Defense_League_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_internet_defense_league_init' );
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,134 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* MailChimp popup widget.
|
||||
* It acts as a wrapper for the mailchimp_subscriber_popup shortcode.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
if ( ! class_exists( 'Jetpack_MailChimp_Subscriber_Popup_Widget' ) ) {
|
||||
|
||||
if ( ! class_exists( 'MailChimp_Subscriber_Popup' ) ) {
|
||||
include_once JETPACK__PLUGIN_DIR . 'modules/shortcodes/mailchimp.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register MailChimp Subscriber Popup widget.
|
||||
*/
|
||||
function jetpack_mailchimp_subscriber_popup_widget_init() {
|
||||
register_widget( 'Jetpack_MailChimp_Subscriber_Popup_Widget' );
|
||||
}
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_mailchimp_subscriber_popup_widget_init' );
|
||||
|
||||
/**
|
||||
* Add a MailChimp subscription form.
|
||||
*/
|
||||
class Jetpack_MailChimp_Subscriber_Popup_Widget extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'widget_mailchimp_subscriber_popup',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'MailChimp Subscriber Popup', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_mailchimp_subscriber_popup',
|
||||
'description' => __( 'Allows displaying a popup subscription form to visitors.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Mailchimp Subscriber Popup" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'widget_mailchimp_subscriber_popup';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the HTML for this widget.
|
||||
*
|
||||
* @param array $args An array of standard parameters for widgets in this theme.
|
||||
* @param array $instance An array of settings for this widget instance.
|
||||
*
|
||||
* @return void Echoes it's output
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, array( 'code' => '' ) );
|
||||
|
||||
// Regular expresion that will match maichimp shortcode.
|
||||
$regex = '(\[mailchimp_subscriber_popup[^\]]+\])';
|
||||
|
||||
// Check if the shortcode exists.
|
||||
preg_match( $regex, $instance['code'], $matches );
|
||||
|
||||
// Process the shortcode only, if exists.
|
||||
if ( ! empty( $matches[0] ) ) {
|
||||
echo do_shortcode( $matches[0] );
|
||||
}
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'mailchimp_subscriber_popup' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with the settings when they are saved by the admin.
|
||||
*
|
||||
* @param array $new_instance New configuration values.
|
||||
* @param array $old_instance Old configuration values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['code'] = MailChimp_Subscriber_Popup::reversal( $new_instance['code'] );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the form for this widget on the Widgets page of the WP Admin area.
|
||||
*
|
||||
* @param array $instance Instance configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, array( 'code' => '' ) );
|
||||
|
||||
$label = sprintf(
|
||||
wp_kses(
|
||||
/* Translators: %s is a link to the MailChimp support docs. */
|
||||
__( 'Code: <a href="%s" target="_blank">( ? )</a>', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
'https://en.support.wordpress.com/mailchimp/'
|
||||
);
|
||||
|
||||
printf(
|
||||
'<p><label for="%1$s">%4$s</label><textarea class="widefat" id="%1$s" name="%2$s" rows="3">%3$s</textarea></p>',
|
||||
esc_attr( $this->get_field_id( 'code' ) ),
|
||||
esc_attr( $this->get_field_name( 'code' ) ),
|
||||
esc_textarea( $instance['code'] ),
|
||||
$label // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Milestone widget loader.
|
||||
*
|
||||
* Everything happens within the folder, but Jetpack loads widgets via a widgets/*.php scheme.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes the milestone widget. This makes it easier to keep the /milestone/ dir content in sync with wpcom.
|
||||
*/
|
||||
require_once __DIR__ . '/milestone/milestone.php';
|
||||
@@ -0,0 +1,27 @@
|
||||
( function ( $ ) {
|
||||
// We could either be in wp-admin/widgets.php or the customizer.
|
||||
var $container = $( '#customize-controls' );
|
||||
|
||||
if ( ! $container.length ) {
|
||||
$container = $( '#wpbody' );
|
||||
}
|
||||
|
||||
$container.on( 'change', '.milestone-type', function () {
|
||||
var $messageWrapper = $( this ).parent().find( '.milestone-message-wrapper' );
|
||||
|
||||
$( this ).find( 'input[type="radio"]:checked' ).val() === 'since'
|
||||
? $messageWrapper.hide()
|
||||
: $messageWrapper.show();
|
||||
} );
|
||||
|
||||
function triggerChange() {
|
||||
$container.find( '.milestone-type' ).trigger( 'change' );
|
||||
}
|
||||
|
||||
// Used when adding widget via customizer or saving settings.
|
||||
$( document ).on( 'widget-added widget-updated', function () {
|
||||
triggerChange();
|
||||
} );
|
||||
|
||||
triggerChange();
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,753 @@
|
||||
<?php
|
||||
/**
|
||||
* Milestone Countdown Widget
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
|
||||
/**
|
||||
* Class Milestone_Widget
|
||||
*/
|
||||
class Milestone_Widget extends WP_Widget {
|
||||
/**
|
||||
* Holding array for widget configuration and localization.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $config_js = array();
|
||||
|
||||
/**
|
||||
* Available time units sorted in descending order.
|
||||
*
|
||||
* @var Array
|
||||
*/
|
||||
protected $available_units = array(
|
||||
'years',
|
||||
'months',
|
||||
'days',
|
||||
'hours',
|
||||
'minutes',
|
||||
'seconds',
|
||||
);
|
||||
|
||||
/**
|
||||
* Milestone_Widget constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget = array(
|
||||
'classname' => 'milestone-widget',
|
||||
'description' => __( 'Display a countdown to a certain date.', 'jetpack' ),
|
||||
);
|
||||
|
||||
parent::__construct(
|
||||
'Milestone_Widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Milestone', 'jetpack' ) ),
|
||||
$widget
|
||||
);
|
||||
|
||||
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_template' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin' ) );
|
||||
add_action( 'wp_footer', array( $this, 'localize_script' ) );
|
||||
|
||||
if ( is_active_widget( false, false, $this->id_base, true ) || is_active_widget( false, false, 'monster', true ) || is_customize_preview() ) {
|
||||
add_action( 'wp_head', array( __CLASS__, 'styles_template' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets.
|
||||
*
|
||||
* @param string $hook_suffix Hook suffix provided by WordPress.
|
||||
*/
|
||||
public static function enqueue_admin( $hook_suffix ) {
|
||||
if ( 'widgets.php' === $hook_suffix ) {
|
||||
wp_enqueue_style( 'milestone-admin', plugin_dir_url( __FILE__ ) . 'style-admin.css', array(), '20201113' );
|
||||
wp_enqueue_script(
|
||||
'milestone-admin-js',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/milestone/admin.min.js',
|
||||
'modules/widgets/milestone/admin.js'
|
||||
),
|
||||
array( 'jquery' ),
|
||||
'20201113',
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the frontend JS.
|
||||
*/
|
||||
public static function enqueue_template() {
|
||||
if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'milestone',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/milestone/milestone.min.js',
|
||||
'modules/widgets/milestone/milestone.js'
|
||||
),
|
||||
array(),
|
||||
'20201113',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the frontend styling.
|
||||
*/
|
||||
public static function styles_template() {
|
||||
global $themecolors;
|
||||
$colors = wp_parse_args(
|
||||
$themecolors,
|
||||
array(
|
||||
'bg' => 'ffffff',
|
||||
'border' => 'cccccc',
|
||||
'text' => '333333',
|
||||
)
|
||||
);
|
||||
?>
|
||||
<style>
|
||||
.milestone-widget {
|
||||
--milestone-text-color: <?php echo self::sanitize_color_hex( $colors['text'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
|
||||
--milestone-bg-color: <?php echo self::sanitize_color_hex( $colors['bg'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
|
||||
--milestone-border-color:<?php echo self::sanitize_color_hex( $colors['border'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a string representing a color in hexadecimal
|
||||
* notation is safe for use in css and database saves.
|
||||
*
|
||||
* @param string $hex Hexademical code to sanitize.
|
||||
* @param string $prefix Prefix for the hex code.
|
||||
*
|
||||
* @return string Color in hexadecimal notation on success - the string "transparent" otherwise.
|
||||
*/
|
||||
public static function sanitize_color_hex( $hex, $prefix = '#' ) {
|
||||
$hex = trim( $hex );
|
||||
|
||||
/* Strip recognized prefixes. */
|
||||
if ( str_starts_with( $hex, '#' ) ) {
|
||||
$hex = substr( $hex, 1 );
|
||||
} elseif ( str_starts_with( $hex, '%23' ) ) {
|
||||
$hex = substr( $hex, 3 );
|
||||
}
|
||||
|
||||
if ( 0 !== preg_match( '/^[0-9a-fA-F]{6}$/', $hex ) ) {
|
||||
return $prefix . $hex;
|
||||
}
|
||||
|
||||
return 'transparent';
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize Front-end Script.
|
||||
*
|
||||
* Print the javascript configuration array only if the
|
||||
* current template has an instance of the widget that
|
||||
* is still counting down. In all other cases, this
|
||||
* function will dequeue milestone.js.
|
||||
*
|
||||
* Hooks into the "wp_footer" action.
|
||||
*/
|
||||
public function localize_script() {
|
||||
if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( self::$config_js['instances'] ) ) {
|
||||
wp_dequeue_script( 'milestone' );
|
||||
return;
|
||||
}
|
||||
self::$config_js['api_root'] = esc_url_raw( rest_url() );
|
||||
wp_localize_script( 'milestone', 'MilestoneConfig', self::$config_js );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Array of default values for the Widget's options.
|
||||
*/
|
||||
public function defaults() {
|
||||
$now = current_datetime();
|
||||
$now_timestamp = $now->getTimestamp();
|
||||
|
||||
return array(
|
||||
'title' => '',
|
||||
'event' => __( 'The Big Day', 'jetpack' ),
|
||||
'unit' => 'automatic',
|
||||
'type' => 'until',
|
||||
'message' => __( 'The big day is here.', 'jetpack' ),
|
||||
'day' => gmdate( 'd', $now_timestamp ),
|
||||
'month' => gmdate( 'm', $now_timestamp ),
|
||||
'year' => gmdate( 'Y', $now_timestamp ),
|
||||
'hour' => 0,
|
||||
'min' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget
|
||||
*
|
||||
* @param array $args Widget args.
|
||||
* @param array $instance Widget instance.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
$this->enqueue_scripts();
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$widget_id = ! empty( $args['widget_id'] ) ? $args['widget_id'] : 'milestone_widget';
|
||||
$data = $this->get_widget_data( $instance );
|
||||
$config = array(
|
||||
'id' => $widget_id,
|
||||
'message' => $data['message'],
|
||||
'refresh' => $data['refresh'],
|
||||
);
|
||||
|
||||
/*
|
||||
* Sidebars may be configured to not expose the `widget_id`. Example: `twentytwenty` footer areas.
|
||||
*
|
||||
* We need our own unique identifier.
|
||||
*/
|
||||
$config['content_id'] = $widget_id . '-content';
|
||||
|
||||
self::$config_js['instances'][] = $config;
|
||||
|
||||
printf( '<div id="%s" class="milestone-content">', esc_html( $config['content_id'] ) );
|
||||
|
||||
echo '<div class="milestone-header">';
|
||||
echo '<strong class="event">' . esc_html( $instance['event'] ) . '</strong>';
|
||||
echo '<span class="date">' . esc_html( date_i18n( get_option( 'date_format' ), $data['milestone'] ) ) . '</span>';
|
||||
echo '</div>';
|
||||
|
||||
echo wp_kses_post( $data['message'] );
|
||||
|
||||
echo '</div><!--milestone-content-->';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'milestone' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue widget styles
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_style(
|
||||
'milestone-widget',
|
||||
plugins_url( 'milestone-widget.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the widget data.
|
||||
*
|
||||
* @param array $instance Widget instance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_widget_data( $instance ) {
|
||||
$data = array();
|
||||
|
||||
$instance = $this->sanitize_instance( $instance );
|
||||
|
||||
$milestone = mktime( $instance['hour'], $instance['min'], 0, $instance['month'], $instance['day'], $instance['year'] );
|
||||
$now = (int) current_time( 'timestamp' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
|
||||
$type = $instance['type'];
|
||||
|
||||
if ( 'since' === $type ) {
|
||||
$diff = (int) floor( $now - $milestone );
|
||||
} else {
|
||||
$diff = (int) floor( $milestone - $now );
|
||||
}
|
||||
|
||||
$data['diff'] = $diff;
|
||||
$data['unit'] = $this->get_unit( $diff, $instance['unit'] );
|
||||
|
||||
// Setting the refresh counter to equal the number of seconds it takes to flip a unit.
|
||||
$refresh_intervals = array(
|
||||
0, // should be YEAR_IN_SECONDS, but doing setTimeout for a year doesn't seem to be logical.
|
||||
0, // same goes for MONTH_IN_SECONDS.
|
||||
DAY_IN_SECONDS,
|
||||
HOUR_IN_SECONDS,
|
||||
MINUTE_IN_SECONDS,
|
||||
1,
|
||||
);
|
||||
|
||||
$data['refresh'] = $refresh_intervals[ array_search( $data['unit'], $this->available_units, true ) ];
|
||||
$data['milestone'] = $milestone;
|
||||
|
||||
if ( ( 1 > $diff ) && ( 'until' === $type ) ) {
|
||||
$data['message'] = '<div class="milestone-message">' . $instance['message'] . '</div>';
|
||||
$data['refresh'] = 0; // No need to refresh, the milestone has been reached.
|
||||
} else {
|
||||
$interval_text = $this->get_interval_in_units( $diff, $data['unit'] );
|
||||
$interval = (int) $interval_text;
|
||||
|
||||
if ( 'since' === $type ) {
|
||||
|
||||
switch ( $data['unit'] ) {
|
||||
case 'years':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of year(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">year ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">years ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'months':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of month(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">month ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">months ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'days':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of days(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">day ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">days ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'hours':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of hours(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">hour ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">hours ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'minutes':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of minutes(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">minute ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">minutes ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'seconds':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of second(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">second ago.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">seconds ago.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch ( $this->get_unit( $diff, $instance['unit'] ) ) {
|
||||
case 'years':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of year(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">year to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">years to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'months':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of month(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">month to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">months to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'days':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of days(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">day to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">days to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'hours':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of hour(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">hour to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">hours to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'minutes':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of minute(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">minute to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">minutes to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
case 'seconds':
|
||||
$data['message'] = sprintf(
|
||||
/* translators: %s is the number of second(s). */
|
||||
_n(
|
||||
'<span class="difference">%s</span> <span class="label">second to go.</span>',
|
||||
'<span class="difference">%s</span> <span class="label">seconds to go.</span>',
|
||||
$interval,
|
||||
'jetpack'
|
||||
),
|
||||
$interval_text
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$data['message'] = '<div class="milestone-countdown">' . $data['message'] . '</div>';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the largest possible time unit that the difference will be displayed in.
|
||||
*
|
||||
* @param Integer $seconds the interval in seconds.
|
||||
* @param String $maximum_unit the maximum unit that will be used. Optional.
|
||||
* @return String $calculated_unit
|
||||
*/
|
||||
protected function get_unit( $seconds, $maximum_unit = 'automatic' ) {
|
||||
$unit = '';
|
||||
|
||||
if ( $seconds >= YEAR_IN_SECONDS * 2 ) {
|
||||
// more than 2 years - show in years, one decimal point.
|
||||
$unit = 'years';
|
||||
|
||||
} elseif ( $seconds >= YEAR_IN_SECONDS ) {
|
||||
if ( 'years' === $maximum_unit ) {
|
||||
$unit = 'years';
|
||||
} else {
|
||||
// automatic mode - showing months even if it's between one and two years.
|
||||
$unit = 'months';
|
||||
}
|
||||
} elseif ( $seconds >= MONTH_IN_SECONDS * 3 ) {
|
||||
// fewer than 2 years - show in months.
|
||||
$unit = 'months';
|
||||
|
||||
} elseif ( $seconds >= MONTH_IN_SECONDS ) {
|
||||
if ( 'months' === $maximum_unit ) {
|
||||
$unit = 'months';
|
||||
} else {
|
||||
// automatic mode - showing days even if it's between one and three months.
|
||||
$unit = 'days';
|
||||
}
|
||||
} elseif ( $seconds >= DAY_IN_SECONDS - 1 ) {
|
||||
// fewer than a month - show in days.
|
||||
$unit = 'days';
|
||||
|
||||
} elseif ( $seconds >= HOUR_IN_SECONDS - 1 ) {
|
||||
// less than 1 day - show in hours.
|
||||
$unit = 'hours';
|
||||
|
||||
} elseif ( $seconds >= MINUTE_IN_SECONDS - 1 ) {
|
||||
// less than 1 hour - show in minutes.
|
||||
$unit = 'minutes';
|
||||
|
||||
} else {
|
||||
// less than 1 minute - show in seconds.
|
||||
$unit = 'seconds';
|
||||
}
|
||||
|
||||
$maximum_unit_index = array_search( $maximum_unit, $this->available_units, true );
|
||||
$unit_index = array_search( $unit, $this->available_units, true );
|
||||
|
||||
if (
|
||||
false === $maximum_unit_index // the maximum unit parameter is automatic.
|
||||
|| $unit_index > $maximum_unit_index // there is not enough seconds for even one maximum time unit.
|
||||
) {
|
||||
return $unit;
|
||||
}
|
||||
return $maximum_unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a time difference value in specified units.
|
||||
*
|
||||
* @param int $seconds Number of seconds.
|
||||
* @param string $units Unit.
|
||||
* @return int $time_in_units.
|
||||
*/
|
||||
protected function get_interval_in_units( $seconds, $units ) {
|
||||
switch ( $units ) {
|
||||
case 'years':
|
||||
$years = $seconds / YEAR_IN_SECONDS;
|
||||
$decimals = abs( round( $years, 1 ) - round( $years ) ) > 0 ? 1 : 0;
|
||||
return number_format_i18n( $years, $decimals );
|
||||
case 'months':
|
||||
return (int) ( $seconds / 60 / 60 / 24 / 30 );
|
||||
case 'days':
|
||||
return (int) ( $seconds / 60 / 60 / 24 + 1 );
|
||||
case 'hours':
|
||||
return (int) ( $seconds / 60 / 60 );
|
||||
case 'minutes':
|
||||
return (int) ( $seconds / 60 + 1 );
|
||||
default:
|
||||
return $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update widget.
|
||||
*
|
||||
* @param array $new_instance New instance of the widget being saved.
|
||||
* @param array $old_instance Previous instance being saved over.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return $this->sanitize_instance( $new_instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that a number is within a certain range.
|
||||
* If the number is too small it will become the possible lowest value.
|
||||
* If the number is too large it will become the possible highest value.
|
||||
*
|
||||
* @param int $n The number to check.
|
||||
* @param int $floor The lowest possible value.
|
||||
* @param int $ceil The highest possible value.
|
||||
*/
|
||||
public function sanitize_range( $n, $floor, $ceil ) {
|
||||
$n = (int) $n;
|
||||
if ( $n < $floor ) {
|
||||
$n = $floor;
|
||||
} elseif ( $n > $ceil ) {
|
||||
$n = $ceil;
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize an instance of this widget.
|
||||
*
|
||||
* Date ranges match the documentation for mktime in the php manual.
|
||||
*
|
||||
* @see https://php.net/manual/en/function.mktime.php#refsect1-function.mktime-parameters
|
||||
*
|
||||
* @uses Milestone_Widget::sanitize_range().
|
||||
*
|
||||
* @param array $dirty Unsantized data for the widget.
|
||||
*
|
||||
* @return array Santized data.
|
||||
*/
|
||||
public function sanitize_instance( $dirty ) {
|
||||
$dirty = wp_parse_args(
|
||||
$dirty,
|
||||
$this->defaults()
|
||||
);
|
||||
|
||||
$allowed_tags = array(
|
||||
'a' => array(
|
||||
'title' => array(),
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
'em' => array( 'title' => array() ),
|
||||
'strong' => array( 'title' => array() ),
|
||||
);
|
||||
|
||||
$clean = array(
|
||||
'title' => trim( wp_strip_all_tags( stripslashes( $dirty['title'] ) ) ),
|
||||
'event' => trim( wp_strip_all_tags( stripslashes( $dirty['event'] ) ) ),
|
||||
'unit' => $dirty['unit'],
|
||||
'type' => $dirty['type'],
|
||||
'message' => wp_kses( $dirty['message'], $allowed_tags ),
|
||||
'year' => $this->sanitize_range( $dirty['year'], 1901, 2037 ),
|
||||
'month' => $this->sanitize_range( $dirty['month'], 1, 12 ),
|
||||
'hour' => $this->sanitize_range( $dirty['hour'], 0, 23 ),
|
||||
'min' => zeroise( $this->sanitize_range( $dirty['min'], 0, 59 ), 2 ),
|
||||
);
|
||||
|
||||
$clean['day'] = $this->sanitize_range( $dirty['day'], 1, gmdate( 't', mktime( 0, 0, 0, $clean['month'], 1, $clean['year'] ) ) );
|
||||
|
||||
return $clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form
|
||||
*
|
||||
* @param array $instance Widget instance.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = $this->sanitize_instance( $instance );
|
||||
|
||||
$units = array(
|
||||
'automatic' => _x( 'Automatic', 'Milestone widget: mode in which the date unit is determined automatically', 'jetpack' ),
|
||||
'years' => _x( 'Years', 'Milestone widget: mode in which the date unit is set to years', 'jetpack' ),
|
||||
'months' => _x( 'Months', 'Milestone widget: mode in which the date unit is set to months', 'jetpack' ),
|
||||
'days' => _x( 'Days', 'Milestone widget: mode in which the date unit is set to days', 'jetpack' ),
|
||||
'hours' => _x( 'Hours', 'Milestone widget: mode in which the date unit is set to hours', 'jetpack' ),
|
||||
);
|
||||
?>
|
||||
<div class="milestone-widget">
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>"><?php esc_html_e( 'Description', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'event' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['event'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<fieldset class="jp-ms-data-time">
|
||||
<legend><?php esc_html_e( 'Date', 'jetpack' ); ?></legend>
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Month', 'jetpack' ); ?></label>
|
||||
<select id="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="month" name="<?php echo esc_attr( $this->get_field_name( 'month' ) ); ?>">
|
||||
<?php
|
||||
global $wp_locale;
|
||||
for ( $i = 1; $i < 13; $i++ ) {
|
||||
$monthnum = zeroise( $i, 2 );
|
||||
printf(
|
||||
'<option value="%s" %s>%s-%s</option>',
|
||||
esc_attr( $monthnum ),
|
||||
selected( $i, $instance['month'], false ),
|
||||
esc_attr( $monthnum ),
|
||||
esc_attr( $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) ) )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Day', 'jetpack' ); ?></label>
|
||||
<input id="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="day" name="<?php echo esc_attr( $this->get_field_name( 'day' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['day'] ); ?>">,
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Year', 'jetpack' ); ?></label>
|
||||
<input id="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="year" name="<?php echo esc_attr( $this->get_field_name( 'year' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['year'] ); ?>">
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="jp-ms-data-time">
|
||||
<legend><?php esc_html_e( 'Time', 'jetpack' ); ?></legend>
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Hour', 'jetpack' ); ?></label>
|
||||
<input id="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="hour" name="<?php echo esc_attr( $this->get_field_name( 'hour' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['hour'] ); ?>">
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Minutes', 'jetpack' ); ?></label>
|
||||
|
||||
<span class="time-separator">:</span>
|
||||
|
||||
<input id="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="minutes" name="<?php echo esc_attr( $this->get_field_name( 'min' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['min'] ); ?>">
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="jp-ms-data-unit">
|
||||
<legend><?php esc_html_e( 'Time Unit', 'jetpack' ); ?></legend>
|
||||
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="assistive-text">
|
||||
<?php esc_html_e( 'Time Unit', 'jetpack' ); ?>
|
||||
</label>
|
||||
|
||||
<select id="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="unit" name="<?php echo esc_attr( $this->get_field_name( 'unit' ) ); ?>">
|
||||
<?php
|
||||
foreach ( $units as $key => $unit ) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr( $key ),
|
||||
selected( $key, $instance['unit'], false ),
|
||||
esc_html( $unit )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<ul class="milestone-type">
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['type'], 'until' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
|
||||
type="radio"
|
||||
value="until"
|
||||
/>
|
||||
<?php esc_html_e( 'Until your milestone', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
<?php checked( $instance['type'], 'since' ); ?>
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
|
||||
type="radio"
|
||||
value="since"
|
||||
/>
|
||||
<?php esc_html_e( 'Since your milestone', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="milestone-message-wrapper">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>"><?php esc_html_e( 'Milestone Reached Message', 'jetpack' ); ?></label>
|
||||
<textarea id="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'message' ) ); ?>" class="widefat" rows="3"><?php echo esc_textarea( $instance['message'] ); ?></textarea>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
.milestone-widget {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.milestone-content {
|
||||
line-height: 2;
|
||||
margin-top: 5px;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.milestone-header {
|
||||
background-color: var(--milestone-text-color, #111);
|
||||
color: var(--milestone-bg-color, #fff);
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
padding: .8em;
|
||||
}
|
||||
.milestone-header .event,
|
||||
.milestone-header .date {
|
||||
display: block;
|
||||
}
|
||||
.milestone-header .event {
|
||||
font-size: 120%;
|
||||
}
|
||||
.milestone-countdown .difference {
|
||||
display: block;
|
||||
font-size: 500%;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.milestone-countdown,
|
||||
.milestone-message {
|
||||
background-color: var(--milestone-bg-color, #fff);
|
||||
border: 1px solid var(--milestone-border-color, #767676);
|
||||
border-top: 0;
|
||||
color: var(--milestone-text-color, #111);
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.milestone-message {
|
||||
padding-top: 1em;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/* global MilestoneConfig */
|
||||
|
||||
var Milestone = ( function () {
|
||||
// eslint-disable-next-line no-shadow
|
||||
var Milestone = function ( args ) {
|
||||
var widget_content = document.getElementById( args.content_id ),
|
||||
id = args.id,
|
||||
refresh = args.refresh * 1000;
|
||||
|
||||
this.timer = function () {
|
||||
var instance = this;
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onload = function () {
|
||||
var response = JSON.parse( xhr.responseText ),
|
||||
httpCheck = xhr.status >= 200 && xhr.status < 300,
|
||||
responseCheck =
|
||||
'undefined' !== typeof response.message && 'undefined' !== typeof response.refresh;
|
||||
|
||||
if ( httpCheck && responseCheck ) {
|
||||
var countdownElement = widget_content.querySelector( '.milestone-countdown' );
|
||||
|
||||
countdownElement.outerHTML = response.message;
|
||||
refresh = response.refresh * 1000;
|
||||
|
||||
if ( ! refresh ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout( function () {
|
||||
instance.timer();
|
||||
}, refresh );
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open( 'GET', MilestoneConfig.api_root + 'jetpack/v4/widgets/' + id );
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
if ( refresh > 0 ) {
|
||||
this.timer();
|
||||
}
|
||||
};
|
||||
return function ( args ) {
|
||||
return new Milestone( args );
|
||||
};
|
||||
} )();
|
||||
|
||||
( function () {
|
||||
var i,
|
||||
MilestoneInstances = {};
|
||||
|
||||
if ( typeof MilestoneConfig === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0; i < MilestoneConfig.instances.length; i++ ) {
|
||||
MilestoneInstances[ i ] = new Milestone( MilestoneConfig.instances[ i ] );
|
||||
}
|
||||
} )();
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* Milestone Widget Loader.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
/**
|
||||
* The widget class.
|
||||
*/
|
||||
require_once __DIR__ . '/class-milestone-widget.php';
|
||||
|
||||
/**
|
||||
* Registers the widget for use!
|
||||
*/
|
||||
function jetpack_register_widget_milestone() {
|
||||
register_widget( 'Milestone_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_register_widget_milestone' );
|
||||
@@ -0,0 +1,71 @@
|
||||
.milestone-widget fieldset {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.milestone-widget fieldset * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jp-ms-data-time input[type="text"] {
|
||||
text-align: right;
|
||||
width: 2.1em;
|
||||
}
|
||||
|
||||
.jp-ms-data-time .month {
|
||||
width: 5.4em;
|
||||
|
||||
}
|
||||
|
||||
.jp-ms-data-time .year[type="text"] {
|
||||
text-align: right;
|
||||
width: 3.2em;
|
||||
}
|
||||
|
||||
.jp-ms-data-time input[type="text"] {
|
||||
text-align: right;
|
||||
width: 3.2em;
|
||||
}
|
||||
|
||||
.jp-ms-data-time .year[type="text"] {
|
||||
width: 4.5em;
|
||||
}
|
||||
|
||||
.jp-ms-data-time .assistive-text,
|
||||
.jp-ms-data-unit .assistive-text {
|
||||
position: absolute !important;
|
||||
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
.jp-ms-data-time input[type="text"],
|
||||
.jp-ms-data-time .year[type="text"] {
|
||||
width: 2.8em;
|
||||
|
||||
}
|
||||
|
||||
.jp-ms-data-time .year[type="text"] {
|
||||
width: 4em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix styles of the Milestone block when it is dispayed as a part of the Legacy Widget block */
|
||||
.wp-block-legacy-widget__edit-form .widget-inside .widget-content .jp-ms-data-time .month {
|
||||
display: inline-block;
|
||||
width: 6.5em;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.wp-block-legacy-widget__edit-form .widget-inside .widget-content .jp-ms-data-time input[type="text"] {
|
||||
display: inline-block;
|
||||
width: 3.2em;
|
||||
}
|
||||
|
||||
.wp-block-legacy-widget__edit-form .widget-inside .widget-content .jp-ms-data-time .year[type="text"] {
|
||||
width: 4.5em;
|
||||
}
|
||||
|
||||
.editor-styles-wrapper ul.milestone-type {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Redirect;
|
||||
|
||||
// Disable direct access/execution to/of the widget code.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Jetpack_My_Community_Widget displays community members of this site.
|
||||
*
|
||||
* A community member is a WordPress.com user that liked or commented on an entry or subscribed to the site.
|
||||
* Requires WordPress.com connection to work. Otherwise it won't be visible in Widgets screen in admin.
|
||||
*/
|
||||
class Jetpack_My_Community_Widget extends WP_Widget {
|
||||
/**
|
||||
* Transient expiration time.
|
||||
*
|
||||
* @var int $expiration
|
||||
*/
|
||||
public static $expiration = 600;
|
||||
|
||||
/**
|
||||
* Default widget title.
|
||||
*
|
||||
* @var string $default_title
|
||||
*/
|
||||
public $default_title;
|
||||
|
||||
/**
|
||||
* Registers the widget with WordPress.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'jetpack_my_community', // Base ID.
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'My Community', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => esc_html__( "Display members of your site's community.", 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$this->default_title = esc_html__( 'Community', 'jetpack' );
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "My Community" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'jetpack_my_community';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheet for grid layout.
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_register_style( 'jetpack-my-community-widget', plugins_url( 'my-community/style.css', __FILE__ ), array(), '20160129' );
|
||||
wp_enqueue_style( 'jetpack-my-community-widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Back end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$title = isset( $instance['title'] ) ? $instance['title'] : false;
|
||||
if ( false === $title ) {
|
||||
$title = $this->default_title;
|
||||
}
|
||||
|
||||
$number = isset( $instance['number'] ) ? (int) $instance['number'] : 10;
|
||||
if ( ! in_array( $number, array( 10, 50 ), true ) ) {
|
||||
$number = 10;
|
||||
}
|
||||
|
||||
$include_likers = isset( $instance['include_likers'] ) ? (bool) $instance['include_likers'] : true;
|
||||
$include_followers = isset( $instance['include_followers'] ) ? (bool) $instance['include_followers'] : true;
|
||||
$include_commenters = isset( $instance['include_commenters'] ) ? (bool) $instance['include_commenters'] : true;
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label><?php esc_html_e( 'Show a maximum of', 'jetpack' ); ?></label>
|
||||
</p>
|
||||
<ul>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>-few" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" type="radio" value="10" <?php checked( '10', $number ); ?> /> <?php esc_html_e( '10 community members', 'jetpack' ); ?></label></li>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>-lots" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" type="radio" value="50" <?php checked( '50', $number ); ?> /> <?php esc_html_e( '50 community members', 'jetpack' ); ?></label></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'include_likers' ) ); ?>">
|
||||
<input type="checkbox" class="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'include_likers' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'include_likers' ) ); ?>" value="1" <?php checked( $include_likers, 1 ); ?> />
|
||||
<?php esc_html_e( 'Include activity from likers', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'include_followers' ) ); ?>">
|
||||
<input type="checkbox" class="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'include_followers' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'include_followers' ) ); ?>" value="1" <?php checked( $include_followers, 1 ); ?> />
|
||||
<?php esc_html_e( 'Include activity from followers', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'include_commenters' ) ); ?>">
|
||||
<input type="checkbox" class="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'include_commenters' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'include_commenters' ) ); ?>" value="1" <?php checked( $include_commenters, 1 ); ?> />
|
||||
<?php esc_html_e( 'Include activity from commenters', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
if ( $instance['title'] === $this->default_title ) {
|
||||
$instance['title'] = false; // Store as false in case of language change.
|
||||
}
|
||||
|
||||
$instance['number'] = (int) $new_instance['number'];
|
||||
if ( ! in_array( $instance['number'], array( 10, 50 ), true ) ) {
|
||||
$instance['number'] = 10;
|
||||
}
|
||||
|
||||
$instance['include_likers'] = (bool) $new_instance['include_likers'];
|
||||
$instance['include_followers'] = (bool) $new_instance['include_followers'];
|
||||
$instance['include_commenters'] = (bool) $new_instance['include_commenters'];
|
||||
|
||||
delete_transient( "$this->id-v2-{$instance['number']}" . (int) $instance['include_likers'] . (int) $instance['include_followers'] . (int) $instance['include_commenters'] );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args(
|
||||
$instance,
|
||||
array(
|
||||
'title' => false,
|
||||
'number' => true,
|
||||
'include_likers' => true,
|
||||
'include_followers' => true,
|
||||
'include_commenters' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$title = $instance['title'];
|
||||
|
||||
if ( false === $title ) {
|
||||
$title = $this->default_title;
|
||||
}
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $title );
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$transient_name = "$this->id-v2-{$instance['number']}" . (int) $instance['include_likers'] . (int) $instance['include_followers'] . (int) $instance['include_commenters'];
|
||||
|
||||
$my_community = get_transient( $transient_name );
|
||||
|
||||
if ( empty( $my_community ) ) {
|
||||
$my_community = $this->get_community( $instance );
|
||||
|
||||
set_transient( $transient_name, $my_community, self::$expiration );
|
||||
}
|
||||
|
||||
echo $my_community; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'my_community' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate request and render the response.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @param array $query Saved widget values from database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_community( $query ) {
|
||||
$members = $this->fetch_remote_community( $query );
|
||||
|
||||
if ( ! empty( $members ) ) {
|
||||
|
||||
$my_community = '<div class="widgets-multi-column-grid"><ul>';
|
||||
|
||||
foreach ( $members as $member ) {
|
||||
$my_community .= sprintf(
|
||||
'<li><a href="%s" title="%s"><img alt="%s" src="%s" class="avatar avatar-48" height="48" width="48"></a></li>',
|
||||
esc_url( $member->profile_URL ), // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
esc_attr( $member->name ),
|
||||
esc_attr( $member->name ),
|
||||
esc_url( $member->avatar_URL ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
);
|
||||
}
|
||||
|
||||
$my_community .= '</ul></div>';
|
||||
|
||||
} elseif ( current_user_can( 'edit_theme_options' ) ) {
|
||||
$my_community = '<p>' . wp_kses(
|
||||
sprintf(
|
||||
/* Translators: 1. link to the widgets settings screen. 2. link to support document. */
|
||||
__( 'There are no users to display in this <a href="%1$s">My Community widget</a>. <a href="%2$s">Want more traffic?</a>', 'jetpack' ),
|
||||
admin_url( 'widgets.php' ),
|
||||
esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) )
|
||||
),
|
||||
array( 'a' => array( 'href' => true ) )
|
||||
) . '</p>';
|
||||
} else {
|
||||
$my_community = '<p>' . esc_html__( "I'm just starting out; leave me a comment or a like :)", 'jetpack' ) . '</p>';
|
||||
}
|
||||
|
||||
return $my_community;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request community members to WordPress.com endpoint.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @param array $query Saved widget values from database.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function fetch_remote_community( $query ) {
|
||||
$jetpack_blog_id = Jetpack_Options::get_option( 'id' );
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'number' => $query['number'],
|
||||
'likers' => (int) $query['include_likers'],
|
||||
'followers' => (int) $query['include_followers'],
|
||||
'commenters' => (int) $query['include_commenters'],
|
||||
),
|
||||
"https://public-api.wordpress.com/rest/v1.1/sites/$jetpack_blog_id/community"
|
||||
);
|
||||
$response = wp_remote_get( $url );
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( empty( $response_body ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$response_body = json_decode( $response_body );
|
||||
|
||||
if ( isset( $response_body->users ) ) {
|
||||
return $response_body->users;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If site is connected to WordPress.com, register the widget.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
function jetpack_my_community_init() {
|
||||
if ( Jetpack::is_connection_ready() ) {
|
||||
register_widget( 'Jetpack_My_Community_Widget' );
|
||||
}
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_my_community_init' );
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Multi-Column Grid Layout */
|
||||
.widgets-multi-column-grid ul {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid ul li {
|
||||
background: none;
|
||||
clear: none;
|
||||
float: left;
|
||||
margin: 0 -5px -3px 0;
|
||||
padding: 0 8px 6px 0;
|
||||
border: none;
|
||||
list-style-type: none !important;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid ul li a {
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid .avatar {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Ensure My Community images fit the 48 pixel grid. */
|
||||
.widget_jetpack_my_community .avatar-48,
|
||||
.widget_jetpack_my_community .avatar-240 {
|
||||
max-width: 48px;
|
||||
max-height: 48px;
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* RSS Links Widget
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* Register the widget.
|
||||
*/
|
||||
function jetpack_rss_links_widget_init() {
|
||||
register_widget( Jetpack_RSS_Links_Widget::class );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_rss_links_widget_init' );
|
||||
|
||||
/**
|
||||
* RSS Links Widget class.
|
||||
*/
|
||||
class Jetpack_RSS_Links_Widget extends WP_Widget {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget_rss_links',
|
||||
'description' => __( "Links to your blog's RSS feeds", 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
);
|
||||
parent::__construct(
|
||||
'rss_links',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'RSS Links', 'jetpack' ) ),
|
||||
$widget_ops
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the widget.
|
||||
*
|
||||
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
|
||||
* @param array $instance The settings for the particular instance of the widget.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults() );
|
||||
|
||||
$before_widget = isset( $args['before_widget'] ) ? $args['before_widget'] : '';
|
||||
$before_title = isset( $args['before_title'] ) ? $args['before_title'] : '';
|
||||
$after_title = isset( $args['after_title'] ) ? $args['after_title'] : '';
|
||||
$after_widget = isset( $args['after_widget'] ) ? $args['after_widget'] : '';
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
echo $before_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( $title ) {
|
||||
echo $before_title . $title . $after_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( 'text' === $instance['format'] ) {
|
||||
echo '<ul>';
|
||||
}
|
||||
|
||||
if ( 'posts' === $instance['display'] ) {
|
||||
$this->rss_link( 'posts', $instance );
|
||||
} elseif ( 'comments' === $instance['display'] ) {
|
||||
$this->rss_link( 'comments', $instance );
|
||||
} elseif ( 'posts-comments' === $instance['display'] ) {
|
||||
$this->rss_link( 'posts', $instance );
|
||||
$this->rss_link( 'comments', $instance );
|
||||
}
|
||||
|
||||
if ( 'text' === $instance['format'] ) {
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo "\n" . $after_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'rss-links' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values
|
||||
* These values are used in new widgets as well as when sanitizing input.
|
||||
*
|
||||
* @return array Array of default values for the Widget's options
|
||||
*/
|
||||
public function defaults() {
|
||||
return array(
|
||||
'title' => '',
|
||||
'display' => 'posts-comments',
|
||||
'format' => 'text',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = $old_instance;
|
||||
|
||||
$instance['title'] = wp_filter_nohtml_kses( $new_instance['title'] );
|
||||
$instance['display'] = $new_instance['display'];
|
||||
$instance['format'] = $new_instance['format'];
|
||||
$instance['imagesize'] = $new_instance['imagesize'];
|
||||
$instance['imagecolor'] = $new_instance['imagecolor'];
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults() );
|
||||
|
||||
$title = stripslashes( $instance['title'] );
|
||||
$display = $instance['display'];
|
||||
$format = $instance['format'];
|
||||
$image_size = isset( $instance['imagesize'] ) ? $instance['imagesize'] : 0;
|
||||
$image_color = isset( $instance['imagecolor'] ) ? $instance['imagecolor'] : 'red';
|
||||
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'title' ) ) . '">' . esc_html__( 'Title:', 'jetpack' ) . '
|
||||
<input class="widefat" id="' . esc_attr( $this->get_field_id( 'title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'title' ) ) . '" type="text" value="' . esc_attr( $title ) . '" />
|
||||
</label></p>';
|
||||
|
||||
$displays = array(
|
||||
'posts' => __( 'Posts', 'jetpack' ),
|
||||
'comments' => __( 'Comments', 'jetpack' ),
|
||||
'posts-comments' => __( 'Posts & Comments', 'jetpack' ),
|
||||
);
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'display' ) ) . '">' . esc_html__( 'Feed(s) to Display:', 'jetpack' ) . '
|
||||
<select class="widefat" id="' . esc_attr( $this->get_field_id( 'display' ) ) . '" name="' . esc_attr( $this->get_field_name( 'display' ) ) . '">';
|
||||
foreach ( $displays as $display_option => $label ) {
|
||||
echo '<option value="' . esc_attr( $display_option ) . '"';
|
||||
if ( $display_option === $display ) {
|
||||
echo ' selected="selected"';
|
||||
}
|
||||
echo '>' . esc_html( $label ) . '</option>' . "\n";
|
||||
}
|
||||
echo '</select></label></p>';
|
||||
|
||||
$formats = array(
|
||||
'text' => __( 'Text Link', 'jetpack' ),
|
||||
'image' => __( 'Image Link', 'jetpack' ),
|
||||
'text-image' => __( 'Text & Image Links', 'jetpack' ),
|
||||
);
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'format' ) ) . '">' . esc_html_x( 'Format:', 'Noun', 'jetpack' ) . '
|
||||
<select class="widefat" id="' . esc_attr( $this->get_field_id( 'format' ) ) . '" name="' . esc_attr( $this->get_field_name( 'format' ) ) . '" onchange="if ( this.value == \'text\' ) jQuery( \'#' . esc_js( $this->get_field_id( 'image-settings' ) ) . '\' ).fadeOut(); else jQuery( \'#' . esc_js( $this->get_field_id( 'image-settings' ) ) . '\' ).fadeIn();">';
|
||||
foreach ( $formats as $format_option => $label ) {
|
||||
echo '<option value="' . esc_attr( $format_option ) . '"';
|
||||
if ( $format_option === $format ) {
|
||||
echo ' selected="selected"';
|
||||
}
|
||||
echo '>' . esc_html( $label ) . '</option>' . "\n";
|
||||
}
|
||||
echo '</select></label></p>';
|
||||
|
||||
echo '<div id="' . esc_attr( $this->get_field_id( 'image-settings' ) ) . '"';
|
||||
if ( 'text' === $format ) {
|
||||
echo ' style="display: none;"';
|
||||
}
|
||||
echo '><h3>' . esc_html__( 'Image Settings:', 'jetpack' ) . '</h3>';
|
||||
|
||||
$sizes = array(
|
||||
'small' => __( 'Small', 'jetpack' ),
|
||||
'medium' => __( 'Medium', 'jetpack' ),
|
||||
'large' => __( 'Large', 'jetpack' ),
|
||||
);
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'imagesize' ) ) . '">' . esc_html__( 'Image Size:', 'jetpack' ) . '
|
||||
<select class="widefat" id="' . esc_attr( $this->get_field_id( 'imagesize' ) ) . '" name="' . esc_attr( $this->get_field_name( 'imagesize' ) ) . '">';
|
||||
foreach ( $sizes as $size => $label ) {
|
||||
echo '<option value="' . esc_attr( $size ) . '"';
|
||||
if ( $size === $image_size ) {
|
||||
echo ' selected="selected"';
|
||||
}
|
||||
echo '>' . esc_html( $label ) . '</option>' . "\n";
|
||||
}
|
||||
echo '</select></label></p>';
|
||||
|
||||
$colors = array(
|
||||
'red' => __( 'Red', 'jetpack' ),
|
||||
'orange' => __( 'Orange', 'jetpack' ),
|
||||
'green' => __( 'Green', 'jetpack' ),
|
||||
'blue' => __( 'Blue', 'jetpack' ),
|
||||
'purple' => __( 'Purple', 'jetpack' ),
|
||||
'pink' => __( 'Pink', 'jetpack' ),
|
||||
'silver' => __( 'Silver', 'jetpack' ),
|
||||
);
|
||||
echo '<p><label for="' . esc_attr( $this->get_field_id( 'imagecolor' ) ) . '">' . esc_html__( 'Image Color:', 'jetpack' ) . '
|
||||
<select class="widefat" id="' . esc_attr( $this->get_field_id( 'imagecolor' ) ) . '" name="' . esc_attr( $this->get_field_name( 'imagecolor' ) ) . '">';
|
||||
foreach ( $colors as $color => $label ) {
|
||||
echo '<option value="' . esc_attr( $color ) . '"';
|
||||
if ( $color === $image_color ) {
|
||||
echo ' selected="selected"';
|
||||
}
|
||||
echo '>' . esc_html( $label ) . '</option>' . "\n";
|
||||
}
|
||||
echo '</select></label></p></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a link with a link to the feed.
|
||||
*
|
||||
* @param string $type Widget type (posts or comments).
|
||||
* @param array $args Widget arguments.
|
||||
*/
|
||||
private function rss_link( $type, $args ) {
|
||||
$link_text = null;
|
||||
$rss_type = null;
|
||||
$subscribe_to = null;
|
||||
|
||||
if ( 'posts' === $type ) {
|
||||
$subscribe_to = esc_html__( 'Subscribe to posts', 'jetpack' );
|
||||
$link_text = esc_html__( 'RSS - Posts', 'jetpack' );
|
||||
$rss_type = 'rss2_url';
|
||||
} elseif ( 'comments' === $type ) {
|
||||
$subscribe_to = esc_html__( 'Subscribe to comments', 'jetpack' );
|
||||
$link_text = esc_html__( 'RSS - Comments', 'jetpack' );
|
||||
$rss_type = 'comments_rss2_url';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the target link attribute for the RSS link in the RSS widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param bool false Control whether the link should open in a new tab. Default to false.
|
||||
*/
|
||||
if ( apply_filters( 'jetpack_rsslinks_widget_target_blank', false ) ) {
|
||||
$link_target = '_blank';
|
||||
} else {
|
||||
$link_target = '_self';
|
||||
}
|
||||
|
||||
$link_contents = null;
|
||||
$format = $args['format'];
|
||||
if ( 'image' === $format ) {
|
||||
$link_contents = $this->get_image_tag( $args );
|
||||
} elseif ( 'text-image' === $format ) {
|
||||
$link_contents = sprintf(
|
||||
'%1$s %2$s',
|
||||
$this->get_image_tag( $args ),
|
||||
$link_text
|
||||
);
|
||||
} elseif ( 'text' === $format ) {
|
||||
$link_contents = $link_text;
|
||||
}
|
||||
|
||||
printf(
|
||||
'%1$s<a target="%3$s" href="%4$s" title="%5$s">%6$s</a>%2$s',
|
||||
'text' === $format ? '<li>' : '<p>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
'text' === $format ? '</li>' : '</p>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
esc_attr( $link_target ),
|
||||
esc_url( get_bloginfo( $rss_type ) ),
|
||||
esc_attr( $subscribe_to ),
|
||||
$link_contents // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- we are escaping this above.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an image tag for the RSS icon.
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
*/
|
||||
private function get_image_tag( $args ) {
|
||||
$image_path = sprintf(
|
||||
'images/rss/%1$s-%2$s.png',
|
||||
$args['imagecolor'],
|
||||
$args['imagesize']
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the image used as RSS icon in the RSS widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $var URL of RSS Widget icon.
|
||||
*/
|
||||
$image = apply_filters(
|
||||
'jetpack_rss_widget_icon',
|
||||
plugins_url( $image_path, dirname( __DIR__ ) )
|
||||
);
|
||||
|
||||
return sprintf(
|
||||
'<img src="%1$s" alt="%2$s" />',
|
||||
esc_url( $image ),
|
||||
esc_attr__( 'RSS feed', 'jetpack' )
|
||||
);
|
||||
}
|
||||
} // Class Jetpack_RSS_Links_Widget
|
||||
@@ -0,0 +1,628 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Tracking;
|
||||
|
||||
// Disable direct access/execution to/of the widget code.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Jetpack_Simple_Payments_Widget' ) ) {
|
||||
/**
|
||||
* Pay with PayPal (aka Simple Payments)
|
||||
*
|
||||
* Display a Pay with PayPal button as a Widget.
|
||||
*/
|
||||
class Jetpack_Simple_Payments_Widget extends WP_Widget {
|
||||
/**
|
||||
* Currencies should be supported by PayPal:
|
||||
*
|
||||
* @var array $supported_currency_list
|
||||
* @link https://developer.paypal.com/docs/api/reference/currency-codes/
|
||||
*
|
||||
* List has to be in sync with list at the block's client side and API's backend side:
|
||||
* @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/extensions/blocks/simple-payments/constants.js#L9-L39
|
||||
* @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/modules/simple-payments/simple-payments.php#L386-L415
|
||||
*
|
||||
* Indian Rupee (INR) is listed here for backwards compatibility with previously added widgets.
|
||||
* It's not supported by Pay with PayPal because at the time of the creation of this file
|
||||
* because it's limited to in-country PayPal India accounts only.
|
||||
* Discussion: https://github.com/Automattic/wp-calypso/pull/28236
|
||||
*/
|
||||
private static $supported_currency_list = array(
|
||||
'USD' => '$',
|
||||
'GBP' => '£',
|
||||
'JPY' => '¥',
|
||||
'BRL' => 'R$',
|
||||
'EUR' => '€',
|
||||
'NZD' => 'NZ$',
|
||||
'AUD' => 'A$',
|
||||
'CAD' => 'C$',
|
||||
'INR' => '₹',
|
||||
'ILS' => '₪',
|
||||
'RUB' => '₽',
|
||||
'MXN' => 'MX$',
|
||||
'SEK' => 'Skr',
|
||||
'HUF' => 'Ft',
|
||||
'CHF' => 'CHF',
|
||||
'CZK' => 'Kč',
|
||||
'DKK' => 'Dkr',
|
||||
'HKD' => 'HK$',
|
||||
'NOK' => 'Kr',
|
||||
'PHP' => '₱',
|
||||
'PLN' => 'PLN',
|
||||
'SGD' => 'S$',
|
||||
'TWD' => 'NT$',
|
||||
'THB' => '฿',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'jetpack_simple_payments_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Pay with PayPal', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'jetpack-simple-payments',
|
||||
'description' => __( 'Add a Pay with PayPal button as a Widget.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
global $pagenow;
|
||||
if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
|
||||
}
|
||||
|
||||
$jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
|
||||
if ( is_customize_preview() && $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
||||
|
||||
add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
|
||||
add_action( 'wp_ajax_customize-jetpack-simple-payments-buttons-get', array( $this, 'ajax_get_payment_buttons' ) );
|
||||
add_action( 'wp_ajax_customize-jetpack-simple-payments-button-save', array( $this, 'ajax_save_payment_button' ) );
|
||||
add_action( 'wp_ajax_customize-jetpack-simple-payments-button-delete', array( $this, 'ajax_delete_payment_button' ) );
|
||||
}
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_simple_payment_widget' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of the widgets hidden from the Legacy Widget block.
|
||||
*
|
||||
* This is used to hide the Pay with PayPal from the Legacy Widget block.
|
||||
*
|
||||
* @param array $widget_types the widget types that are currently hidden.
|
||||
* @return array Widget types hidden from the Legacy Widget block
|
||||
*/
|
||||
public function hide_simple_payment_widget( $widget_types ) {
|
||||
$widget_types[] = 'jetpack_simple_payments_widget';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of default values.
|
||||
*
|
||||
* These values are used in new widgets.
|
||||
*
|
||||
* @return array Default values for the widget options.
|
||||
*/
|
||||
private function defaults() {
|
||||
$current_user = wp_get_current_user();
|
||||
$default_product_id = $this->get_first_product_id();
|
||||
|
||||
return array(
|
||||
'title' => '',
|
||||
'product_post_id' => $default_product_id,
|
||||
'form_action' => '',
|
||||
'form_product_id' => 0,
|
||||
'form_product_title' => '',
|
||||
'form_product_description' => '',
|
||||
'form_product_image_id' => 0,
|
||||
'form_product_image_src' => '',
|
||||
'form_product_currency' => '',
|
||||
'form_product_price' => '',
|
||||
'form_product_multiple' => '',
|
||||
'form_product_email' => $current_user->user_email,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a nonce for customizing menus.
|
||||
*
|
||||
* @param array $nonces Array of nonces.
|
||||
* @return array $nonces Modified array of nonces.
|
||||
*/
|
||||
public function filter_nonces( $nonces ) {
|
||||
$nonces['customize-jetpack-simple-payments'] = wp_create_nonce( 'customize-jetpack-simple-payments' );
|
||||
return $nonces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles.
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_enqueue_style( 'jetpack-simple-payments-widget-style', plugins_url( 'simple-payments/style.css', __FILE__ ), array(), '20180518' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin styles.
|
||||
*/
|
||||
public function admin_enqueue_styles() {
|
||||
wp_enqueue_style(
|
||||
'jetpack-simple-payments-widget-customizer',
|
||||
plugins_url( 'simple-payments/customizer.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts.
|
||||
*/
|
||||
public function admin_enqueue_scripts() {
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script(
|
||||
'jetpack-simple-payments-widget-customizer',
|
||||
plugins_url( '/simple-payments/customizer.js', __FILE__ ),
|
||||
array( 'jquery' ),
|
||||
JETPACK__VERSION,
|
||||
true
|
||||
);
|
||||
wp_localize_script(
|
||||
'jetpack-simple-payments-widget-customizer',
|
||||
'jpSimplePaymentsStrings',
|
||||
array(
|
||||
'deleteConfirmation' => __( 'Are you sure you want to delete this item? It will be disabled and removed from all locations where it currently appears.', 'jetpack' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payment buttons.
|
||||
*/
|
||||
public function ajax_get_payment_buttons() {
|
||||
if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
|
||||
wp_send_json_error( 'bad_nonce', 400 );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'customize' ) ) {
|
||||
wp_send_json_error( 'customize_not_allowed', 403 );
|
||||
}
|
||||
|
||||
$post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
|
||||
if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
|
||||
wp_send_json_error( 'insufficient_post_permissions', 403 );
|
||||
}
|
||||
|
||||
$product_posts = get_posts(
|
||||
array(
|
||||
'numberposts' => 100,
|
||||
'orderby' => 'date',
|
||||
'post_type' => Jetpack_Simple_Payments::$post_type_product,
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
$formatted_products = array_map( array( $this, 'format_product_post_for_ajax_reponse' ), $product_posts );
|
||||
|
||||
wp_send_json_success( $formatted_products );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format product_post object.
|
||||
*
|
||||
* @param object $product_post - info about the post the product is on.
|
||||
*/
|
||||
public function format_product_post_for_ajax_reponse( $product_post ) {
|
||||
return array(
|
||||
'ID' => $product_post->ID,
|
||||
'post_title' => $product_post->post_title,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle saving the simple payments widget.
|
||||
*/
|
||||
public function ajax_save_payment_button() {
|
||||
if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
|
||||
wp_send_json_error( 'bad_nonce', 400 );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'customize' ) ) {
|
||||
wp_send_json_error( 'customize_not_allowed', 403 );
|
||||
}
|
||||
|
||||
$post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
|
||||
if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
|
||||
wp_send_json_error( 'insufficient_post_permissions', 403 );
|
||||
}
|
||||
|
||||
if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
|
||||
wp_send_json_error( 'missing_params', 400 );
|
||||
}
|
||||
|
||||
$params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated by validate_ajax_params().
|
||||
$errors = $this->validate_ajax_params( $params );
|
||||
if ( ! empty( $errors->errors ) ) {
|
||||
wp_send_json_error( $errors );
|
||||
}
|
||||
|
||||
$product_post_id = isset( $params['product_post_id'] ) ? (int) $params['product_post_id'] : 0;
|
||||
|
||||
$product_post = array(
|
||||
'ID' => $product_post_id,
|
||||
'post_type' => Jetpack_Simple_Payments::$post_type_product,
|
||||
'post_status' => 'publish',
|
||||
'post_title' => $params['post_title'],
|
||||
'post_content' => $params['post_content'],
|
||||
'_thumbnail_id' => ! empty( $params['image_id'] ) ? $params['image_id'] : -1,
|
||||
'meta_input' => array(
|
||||
'spay_currency' => $params['currency'],
|
||||
'spay_price' => $params['price'],
|
||||
'spay_multiple' => isset( $params['multiple'] ) ? (int) $params['multiple'] : 0,
|
||||
'spay_email' => is_email( $params['email'] ),
|
||||
),
|
||||
);
|
||||
|
||||
if ( empty( $product_post_id ) ) {
|
||||
$product_post_id = wp_insert_post( $product_post );
|
||||
} else {
|
||||
$product_post_id = wp_update_post( $product_post );
|
||||
}
|
||||
|
||||
if ( ! $product_post_id || is_wp_error( $product_post_id ) ) {
|
||||
wp_send_json_error( $product_post_id );
|
||||
}
|
||||
|
||||
$tracks_properties = array(
|
||||
'id' => $product_post_id,
|
||||
'currency' => $params['currency'],
|
||||
'price' => $params['price'],
|
||||
);
|
||||
if ( 0 === $product_post['ID'] ) {
|
||||
$this->record_event( 'created', 'create', $tracks_properties );
|
||||
} else {
|
||||
$this->record_event( 'updated', 'update', $tracks_properties );
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'product_post_id' => $product_post_id,
|
||||
'product_post_title' => $params['post_title'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle deleting the simple payment widget.
|
||||
*/
|
||||
public function ajax_delete_payment_button() {
|
||||
if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
|
||||
wp_send_json_error( 'bad_nonce', 400 );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'customize' ) ) {
|
||||
wp_send_json_error( 'customize_not_allowed', 403 );
|
||||
}
|
||||
|
||||
if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
|
||||
wp_send_json_error( 'missing_params', 400 );
|
||||
}
|
||||
|
||||
$params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated just below.
|
||||
$illegal_params = array_diff( array_keys( $params ), array( 'product_post_id' ) );
|
||||
if ( ! empty( $illegal_params ) ) {
|
||||
wp_send_json_error( 'illegal_params', 400 );
|
||||
}
|
||||
|
||||
$product_id = (int) $params['product_post_id'];
|
||||
$product_post = get_post( $product_id );
|
||||
|
||||
$return = array( 'status' => $product_post->post_status );
|
||||
|
||||
wp_delete_post( $product_id, true );
|
||||
$status = get_post_status( $product_id );
|
||||
if ( false === $status ) {
|
||||
$return['status'] = 'deleted';
|
||||
}
|
||||
|
||||
$this->record_event( 'deleted', 'delete', array( 'id' => $product_id ) );
|
||||
|
||||
wp_send_json_success( $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of decimal places on string representing a price.
|
||||
*
|
||||
* @param string $number Price to check.
|
||||
* @return int|null number of decimal places.
|
||||
*/
|
||||
private function get_decimal_places( $number ) {
|
||||
$parts = explode( '.', $number );
|
||||
if ( count( $parts ) > 2 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return isset( $parts[1] ) ? strlen( $parts[1] ) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate ajax parameters.
|
||||
*
|
||||
* @param array $params - the parameters.
|
||||
*/
|
||||
public function validate_ajax_params( $params ) {
|
||||
$errors = new WP_Error();
|
||||
|
||||
$illegal_params = array_diff( array_keys( $params ), array( 'product_post_id', 'post_title', 'post_content', 'image_id', 'currency', 'price', 'multiple', 'email' ) );
|
||||
if ( ! empty( $illegal_params ) ) {
|
||||
$errors->add( 'illegal_params', __( 'Invalid parameters.', 'jetpack' ) );
|
||||
}
|
||||
|
||||
if ( empty( $params['post_title'] ) ) {
|
||||
$errors->add( 'post_title', __( "People need to know what they're paying for! Please add a brief title.", 'jetpack' ) );
|
||||
}
|
||||
|
||||
if ( empty( $params['price'] ) || ! is_numeric( $params['price'] ) || (float) $params['price'] <= 0 ) {
|
||||
$errors->add( 'price', __( 'Everything comes with a price tag these days. Please add a your product price.', 'jetpack' ) );
|
||||
}
|
||||
|
||||
// Japan's Yen is the only supported currency with a zero decimal precision.
|
||||
$precision = strtoupper( $params['currency'] ) === 'JPY' ? 0 : 2;
|
||||
$price_decimal_places = $this->get_decimal_places( $params['price'] );
|
||||
if ( $price_decimal_places === null || $price_decimal_places > $precision ) {
|
||||
$errors->add( 'price', __( 'Invalid price', 'jetpack' ) );
|
||||
}
|
||||
|
||||
if ( empty( $params['email'] ) || ! is_email( $params['email'] ) ) {
|
||||
$errors->add( 'email', __( 'We want to make sure payments reach you, so please add an email address.', 'jetpack' ) );
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the first product.
|
||||
*/
|
||||
public function get_first_product_id() {
|
||||
$product_posts = get_posts(
|
||||
array(
|
||||
'numberposts' => 1,
|
||||
'orderby' => 'date',
|
||||
'post_type' => Jetpack_Simple_Payments::$post_type_product,
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
return ! empty( $product_posts ) ? $product_posts[0]->ID : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'] );
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
echo '<div class="jetpack-simple-payments-content">';
|
||||
|
||||
if ( ! empty( $instance['form_action'] ) && in_array( $instance['form_action'], array( 'add', 'edit' ), true ) && is_customize_preview() ) {
|
||||
require __DIR__ . '/simple-payments/widget.php';
|
||||
} else {
|
||||
$jsp = Jetpack_Simple_Payments::get_instance();
|
||||
$simple_payments_button = $jsp->parse_shortcode(
|
||||
array(
|
||||
'id' => $instance['product_post_id'],
|
||||
)
|
||||
);
|
||||
|
||||
if ( $simple_payments_button !== null || is_customize_preview() ) {
|
||||
echo $simple_payments_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
|
||||
echo '</div><!--simple-payments-->';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is already documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'simple_payments' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latests field value from either the old instance or the new instance.
|
||||
*
|
||||
* @param array $new_instance mixed Array of values for the new form instance.
|
||||
* @param array $old_instance mixed Array of values for the old form instance.
|
||||
* @param mixed $field mixed Field value.
|
||||
*/
|
||||
private function get_latest_field_value( $new_instance, $old_instance, $field ) {
|
||||
return ! empty( $new_instance[ $field ] )
|
||||
? sanitize_text_field( $new_instance[ $field ] )
|
||||
: $old_instance[ $field ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the product fields from the product post. If no post found
|
||||
* it returns the default values.
|
||||
*
|
||||
* @param int $product_post_id Product Post ID.
|
||||
* @return array $fields Product Fields from the Product Post.
|
||||
*/
|
||||
private function get_product_from_post( $product_post_id ) {
|
||||
$product_post = get_post( $product_post_id );
|
||||
$form_product_id = $product_post_id;
|
||||
if ( ! empty( $product_post ) ) {
|
||||
$form_product_image_id = get_post_thumbnail_id( $product_post_id );
|
||||
|
||||
return array(
|
||||
'form_product_id' => $form_product_id,
|
||||
'form_product_title' => get_the_title( $product_post ),
|
||||
'form_product_description' => $product_post->post_content,
|
||||
'form_product_image_id' => $form_product_image_id,
|
||||
'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
|
||||
'form_product_currency' => get_post_meta( $product_post_id, 'spay_currency', true ),
|
||||
'form_product_price' => get_post_meta( $product_post_id, 'spay_price', true ),
|
||||
'form_product_multiple' => get_post_meta( $product_post_id, 'spay_multiple', true ) || '0',
|
||||
'form_product_email' => get_post_meta( $product_post_id, 'spay_email', true ),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->defaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a Track event and bump a MC stat.
|
||||
*
|
||||
* @param string $stat_name - the name of the stat.
|
||||
* @param string $event_action - the action we're recording.
|
||||
* @param array $event_properties - proprties of the event.
|
||||
*/
|
||||
private function record_event( $stat_name, $event_action, $event_properties = array() ) {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
// `bumps_stats_extra` only exists on .com
|
||||
if ( function_exists( 'bump_stats_extras' ) ) {
|
||||
require_lib( 'tracks/client' );
|
||||
tracks_record_event( $current_user, 'simple_payments_button_' . $event_action, $event_properties );
|
||||
/** This action is documented in modules/widgets/social-media-icons.php */
|
||||
do_action( 'jetpack_bump_stats_extra', 'jetpack-simple_payments', $stat_name );
|
||||
return;
|
||||
}
|
||||
|
||||
$tracking = new Tracking();
|
||||
$tracking->tracks_record_event( $current_user, 'jetpack_wpa_simple_payments_button_' . $event_action, $event_properties );
|
||||
$jetpack = Jetpack::init();
|
||||
// $jetpack->stat automatically prepends the stat group with 'jetpack-'
|
||||
$jetpack->stat( 'simple_payments', $stat_name );
|
||||
$jetpack->do_stats( 'server_side' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$defaults = $this->defaults();
|
||||
// do not overrite `product_post_id` for `$new_instance` with the defaults.
|
||||
$new_instance = wp_parse_args( $new_instance, array_diff_key( $defaults, array( 'product_post_id' => 0 ) ) );
|
||||
$old_instance = wp_parse_args( $old_instance, $defaults );
|
||||
|
||||
$required_widget_props = array(
|
||||
'title' => $this->get_latest_field_value( $new_instance, $old_instance, 'title' ),
|
||||
'product_post_id' => $this->get_latest_field_value( $new_instance, $old_instance, 'product_post_id' ),
|
||||
'form_action' => $this->get_latest_field_value( $new_instance, $old_instance, 'form_action' ),
|
||||
);
|
||||
|
||||
if ( strcmp( $new_instance['form_action'], $old_instance['form_action'] ) !== 0 ) {
|
||||
if ( 'edit' === $new_instance['form_action'] ) {
|
||||
return array_merge( $this->get_product_from_post( (int) $old_instance['product_post_id'] ), $required_widget_props );
|
||||
}
|
||||
|
||||
if ( 'clear' === $new_instance['form_action'] ) {
|
||||
return array_merge( $this->defaults(), $required_widget_props );
|
||||
}
|
||||
}
|
||||
|
||||
$form_product_image_id = (int) $new_instance['form_product_image_id'];
|
||||
|
||||
$form_product_email = ! empty( $new_instance['form_product_email'] )
|
||||
? sanitize_text_field( $new_instance['form_product_email'] )
|
||||
: $defaults['form_product_email'];
|
||||
|
||||
return array_merge(
|
||||
$required_widget_props,
|
||||
array(
|
||||
'form_product_id' => (int) $new_instance['form_product_id'],
|
||||
'form_product_title' => sanitize_text_field( $new_instance['form_product_title'] ),
|
||||
'form_product_description' => sanitize_text_field( $new_instance['form_product_description'] ),
|
||||
'form_product_image_id' => $form_product_image_id,
|
||||
'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
|
||||
'form_product_currency' => sanitize_text_field( $new_instance['form_product_currency'] ),
|
||||
'form_product_price' => sanitize_text_field( $new_instance['form_product_price'] ),
|
||||
'form_product_multiple' => sanitize_text_field( $new_instance['form_product_multiple'] ),
|
||||
'form_product_email' => $form_product_email,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @html-template-var array $instance
|
||||
* @html-template-var WP_Post[] $product_posts
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
|
||||
if ( ! method_exists( $jetpack_simple_payments, 'is_enabled_jetpack_simple_payments' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
|
||||
require __DIR__ . '/simple-payments/admin-warning.php';
|
||||
return;
|
||||
}
|
||||
|
||||
$instance = wp_parse_args( $instance, $this->defaults() );
|
||||
|
||||
$product_posts = get_posts( // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
array(
|
||||
'numberposts' => 100,
|
||||
'orderby' => 'date',
|
||||
'post_type' => Jetpack_Simple_Payments::$post_type_product,
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
require __DIR__ . '/simple-payments/form.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Jetpack_Simple_Payments_Widget widget.
|
||||
*/
|
||||
function register_widget_jetpack_simple_payments() {
|
||||
if ( ! class_exists( 'Jetpack_Simple_Payments' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
|
||||
if ( ! $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
register_widget( 'Jetpack_Simple_Payments_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'register_widget_jetpack_simple_payments' );
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Display the Pay with PayPal Form.
|
||||
*
|
||||
* @html-template Jetpack_Simple_Payments_Widget::form
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<div class='jetpack-simple-payments-disabled-error'>
|
||||
<p>
|
||||
<?php
|
||||
/**
|
||||
* Show error and help if Pay with PayPal is disabled.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
$support_url = ( defined( 'IS_WPCOM' ) && IS_WPCOM )
|
||||
? 'https://wordpress.com/support/pay-with-paypal/'
|
||||
: 'https://jetpack.com/support/pay-with-paypal/';
|
||||
printf(
|
||||
wp_kses(
|
||||
// translators: variable is a link to the support page.
|
||||
__( 'Your plan doesn\'t include Pay with PayPal. <a href="%s" rel="noopener noreferrer" target="_blank">Learn more and upgrade</a>.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'rel' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( $support_url )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,80 @@
|
||||
.widget-content .jetpack-simple-payments,
|
||||
.widget-content .jetpack-simple-payments-form {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-disabled-error {
|
||||
background: #fff;
|
||||
border-left: 4px solid #dc3232;
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
||||
margin: 5px 0 15px;
|
||||
padding: 1px 12px;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-form .invalid {
|
||||
border: 1px solid #d63638;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-form .cost label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-image-fieldset {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-image-fieldset .placeholder {
|
||||
border: 1px dashed #c3c4c7;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 9px 0;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin: 4px 0 1em;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-image {
|
||||
max-width: 100%;
|
||||
margin-top: 4px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-image img {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed #c3c4c7;
|
||||
padding: 4px;
|
||||
height: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-image img:hover {
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-form .field-currency {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-form .field-price {
|
||||
display: inline-block;
|
||||
line-height: 20px;
|
||||
width: 58%;
|
||||
}
|
||||
|
||||
.widget-content .jetpack-simple-payments-form .alignleft button,
|
||||
.widget-content .jetpack-simple-payments-form .alignright span {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.widget-content .button-link:disabled,
|
||||
.widget-content .button-link:hover[disabled] {
|
||||
color: #a7aaad;
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
/* global jpSimplePaymentsStrings */
|
||||
/* eslint no-var: 0, quote-props: 0 */
|
||||
|
||||
( function ( api, wp, $ ) {
|
||||
var $document = $( document );
|
||||
|
||||
$document.ready( function () {
|
||||
$document.on( 'widget-added', function ( event, widgetContainer ) {
|
||||
if ( widgetContainer.is( '[id*="jetpack_simple_payments_widget"]' ) ) {
|
||||
initWidget( widgetContainer );
|
||||
}
|
||||
} );
|
||||
|
||||
$document.on( 'widget-synced widget-updated', function ( event, widgetContainer ) {
|
||||
//this fires for all widgets, this prevent errors for non SP widgets
|
||||
if ( ! widgetContainer.is( '[id*="jetpack_simple_payments_widget"]' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
syncProductLists();
|
||||
|
||||
var widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' );
|
||||
|
||||
enableFormActions( widgetForm );
|
||||
|
||||
updateProductImage( widgetForm );
|
||||
} );
|
||||
} );
|
||||
|
||||
function initWidget( widgetContainer ) {
|
||||
var widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' );
|
||||
|
||||
//Add New Button
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-add-product' )
|
||||
.on( 'click', showAddNewForm( widgetForm ) );
|
||||
//Edit Button
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-edit-product' )
|
||||
.on( 'click', showEditForm( widgetForm ) );
|
||||
//Select an Image
|
||||
widgetForm
|
||||
.find(
|
||||
'.jetpack-simple-payments-image-fieldset .placeholder, .jetpack-simple-payments-image > img'
|
||||
)
|
||||
.on( 'click', selectImage( widgetForm ) );
|
||||
//Remove Image Button
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-remove-image' )
|
||||
.on( 'click', removeImage( widgetForm ) );
|
||||
//Save Product button
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-save-product' )
|
||||
.on( 'click', saveChanges( widgetForm ) );
|
||||
//Cancel Button
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-cancel-form' )
|
||||
.on( 'click', clearForm( widgetForm ) );
|
||||
//Delete Selected Product
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-delete-product' )
|
||||
.on( 'click', deleteProduct( widgetForm ) );
|
||||
//Input, Select and Checkbox change
|
||||
widgetForm.find( 'select, input, textarea, checkbox' ).on(
|
||||
'change input propertychange',
|
||||
_.debounce( function () {
|
||||
disableFormActions( widgetForm );
|
||||
}, 250 )
|
||||
);
|
||||
}
|
||||
|
||||
function syncProductLists() {
|
||||
var request = wp.ajax.post( 'customize-jetpack-simple-payments-buttons-get', {
|
||||
'customize-jetpack-simple-payments-nonce':
|
||||
api.settings.nonce[ 'customize-jetpack-simple-payments' ],
|
||||
customize_changeset_uuid: api.settings.changeset.uuid,
|
||||
} );
|
||||
|
||||
request.done( function ( data ) {
|
||||
var selectedProduct = 0;
|
||||
|
||||
$( document )
|
||||
.find( 'select.jetpack-simple-payments-products' )
|
||||
.each( function ( index, select ) {
|
||||
var $select = $( select );
|
||||
selectedProduct = $select.val();
|
||||
|
||||
$select.find( 'option' ).remove();
|
||||
$select.append(
|
||||
$.map( data, function ( product ) {
|
||||
return $( '<option>', { value: product.ID, text: product.post_title } );
|
||||
} )
|
||||
);
|
||||
$select.val( selectedProduct );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
function showForm( widgetForm ) {
|
||||
//reset validations
|
||||
widgetForm.find( '.invalid' ).removeClass( 'invalid' );
|
||||
//disable widget title and product selector
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-widget-title' )
|
||||
.add( '.jetpack-simple-payments-products' )
|
||||
//disable add and edit buttons
|
||||
.add( '.jetpack-simple-payments-add-product' )
|
||||
.add( '.jetpack-simple-payments-edit-product' )
|
||||
//disable save, delete and cancel until the widget update event is fired
|
||||
.add( '.jetpack-simple-payments-save-product' )
|
||||
.add( '.jetpack-simple-payments-cancel-form' )
|
||||
.add( '.jetpack-simple-payments-delete-product' )
|
||||
.attr( 'disabled', 'disabled' );
|
||||
//show form
|
||||
widgetForm.find( '.jetpack-simple-payments-form' ).show();
|
||||
}
|
||||
|
||||
function hideForm( widgetForm ) {
|
||||
//enable widget title and product selector
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-widget-title' )
|
||||
.add( '.jetpack-simple-payments-products' )
|
||||
.removeAttr( 'disabled' );
|
||||
//hide the form
|
||||
widgetForm.find( '.jetpack-simple-payments-form' ).hide();
|
||||
}
|
||||
|
||||
function changeFormAction( widgetForm, action ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-form-action' ).val( action ).change();
|
||||
}
|
||||
|
||||
function showAddNewForm( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
showForm( widgetForm );
|
||||
changeFormAction( widgetForm, 'add' );
|
||||
};
|
||||
}
|
||||
|
||||
function showEditForm( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
showForm( widgetForm );
|
||||
changeFormAction( widgetForm, 'edit' );
|
||||
};
|
||||
}
|
||||
|
||||
function clearForm( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
hideForm( widgetForm );
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-add-product, .jetpack-simple-payments-edit-product' )
|
||||
.attr( 'disabled', 'disabled' );
|
||||
changeFormAction( widgetForm, 'clear' );
|
||||
};
|
||||
}
|
||||
|
||||
function enableFormActions( widgetForm ) {
|
||||
var isFormVisible = widgetForm.find( '.jetpack-simple-payments-form' ).is( ':visible' );
|
||||
var isProductSelectVisible = widgetForm
|
||||
.find( '.jetpack-simple-payments-products' )
|
||||
.is( ':visible' ); //areProductsVisible ?
|
||||
var isEdit = widgetForm.find( '.jetpack-simple-payments-form-action' ).val() === 'edit';
|
||||
|
||||
if ( isFormVisible ) {
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-save-product' )
|
||||
.add( '.jetpack-simple-payments-cancel-form' )
|
||||
.removeAttr( 'disabled' );
|
||||
} else {
|
||||
widgetForm.find( '.jetpack-simple-payments-add-product' ).removeAttr( 'disabled' );
|
||||
}
|
||||
|
||||
if ( isFormVisible && isEdit ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-delete-product' ).removeAttr( 'disabled' );
|
||||
}
|
||||
|
||||
if ( isProductSelectVisible && ! isFormVisible ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-edit-product' ).removeAttr( 'disabled' );
|
||||
}
|
||||
}
|
||||
|
||||
function disableFormActions( widgetForm ) {
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-add-product' )
|
||||
.add( '.jetpack-simple-payments-edit-product' )
|
||||
.add( '.jetpack-simple-payments-save-product' )
|
||||
.add( '.jetpack-simple-payments-cancel-form' )
|
||||
.add( '.jetpack-simple-payments-delete-product' )
|
||||
.attr( 'disabled', 'disabled' );
|
||||
}
|
||||
|
||||
function selectImage( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
var imageContainer = widgetForm.find( '.jetpack-simple-payments-image' );
|
||||
|
||||
var mediaFrame = new wp.media.view.MediaFrame.Select( {
|
||||
title: 'Choose Product Image',
|
||||
multiple: false,
|
||||
library: { type: 'image' },
|
||||
button: { text: 'Choose Image' },
|
||||
} );
|
||||
|
||||
mediaFrame.on( 'select', function () {
|
||||
var selection = mediaFrame.state().get( 'selection' ).first().toJSON();
|
||||
//hide placeholder
|
||||
widgetForm.find( '.jetpack-simple-payments-image-fieldset .placeholder' ).hide();
|
||||
|
||||
//load image from media library
|
||||
imageContainer.find( 'img' ).attr( 'src', selection.url ).show();
|
||||
|
||||
//show image and remove button
|
||||
widgetForm.find( '.jetpack-simple-payments-image' ).show();
|
||||
|
||||
//set hidden field for the selective refresh
|
||||
widgetForm.find( '.jetpack-simple-payments-form-image-id' ).val( selection.id ).change();
|
||||
} );
|
||||
|
||||
mediaFrame.open();
|
||||
};
|
||||
}
|
||||
|
||||
function removeImage( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
//show placeholder
|
||||
widgetForm.find( '.jetpack-simple-payments-image-fieldset .placeholder' ).show();
|
||||
|
||||
//hide image and remove button
|
||||
widgetForm.find( '.jetpack-simple-payments-image' ).hide();
|
||||
|
||||
//set hidden field for the selective refresh
|
||||
widgetForm.find( '.jetpack-simple-payments-form-image-id' ).val( '' ).change();
|
||||
};
|
||||
}
|
||||
|
||||
function updateProductImage( widgetForm ) {
|
||||
var newImageId = parseInt(
|
||||
widgetForm.find( '.jetpack-simple-payments-form-image-id' ).val(),
|
||||
10
|
||||
);
|
||||
var newImageSrc = widgetForm.find( '.jetpack-simple-payments-form-image-src' ).val();
|
||||
|
||||
var placeholder = widgetForm.find( '.jetpack-simple-payments-image-fieldset .placeholder' );
|
||||
var image = widgetForm.find( '.jetpack-simple-payments-image > img' );
|
||||
var imageControls = widgetForm.find( '.jetpack-simple-payments-image' );
|
||||
|
||||
if ( newImageId && newImageSrc ) {
|
||||
image.attr( 'src', newImageSrc );
|
||||
placeholder.hide();
|
||||
imageControls.show();
|
||||
} else {
|
||||
placeholder.show();
|
||||
image.removeAttr( 'src' );
|
||||
imageControls.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function decimalPlaces( number ) {
|
||||
var parts = number.split( '.' );
|
||||
if ( parts.length > 2 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parts[ 1 ] ? parts[ 1 ].length : 0;
|
||||
}
|
||||
|
||||
function isFormValid( widgetForm ) {
|
||||
widgetForm.find( '.invalid' ).removeClass( 'invalid' );
|
||||
|
||||
var errors = false;
|
||||
|
||||
var postTitle = widgetForm.find( '.jetpack-simple-payments-form-product-title' ).val();
|
||||
if ( ! postTitle ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-title' ).addClass( 'invalid' );
|
||||
errors = true;
|
||||
}
|
||||
|
||||
var productPrice = widgetForm.find( '.jetpack-simple-payments-form-product-price' ).val();
|
||||
if ( ! productPrice || isNaN( productPrice ) || parseFloat( productPrice ) <= 0 ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-price' ).addClass( 'invalid' );
|
||||
errors = true;
|
||||
}
|
||||
|
||||
// Japan's Yen is the only supported currency with a zero decimal precision.
|
||||
var precision =
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-currency' ).val() === 'JPY' ? 0 : 2;
|
||||
var priceDecimalPlaces = decimalPlaces( productPrice );
|
||||
if ( priceDecimalPlaces === null || priceDecimalPlaces > precision ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-price' ).addClass( 'invalid' );
|
||||
errors = true;
|
||||
}
|
||||
|
||||
var productEmail = widgetForm.find( '.jetpack-simple-payments-form-product-email' ).val();
|
||||
var isProductEmailValid =
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/^((([a-z]|\d|[!#$%&'*+\-/=?^_`{|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#$%&'*+\-/=?^_`{|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(
|
||||
productEmail
|
||||
);
|
||||
if ( ! productEmail || ! isProductEmailValid ) {
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-email' ).addClass( 'invalid' );
|
||||
errors = true;
|
||||
}
|
||||
|
||||
return ! errors;
|
||||
}
|
||||
|
||||
function saveChanges( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
if ( ! isFormValid( widgetForm ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var productPostId = widgetForm.find( '.jetpack-simple-payments-form-product-id' ).val();
|
||||
|
||||
disableFormActions( widgetForm );
|
||||
|
||||
widgetForm.find( '.spinner' ).show();
|
||||
|
||||
var request = wp.ajax.post( 'customize-jetpack-simple-payments-button-save', {
|
||||
'customize-jetpack-simple-payments-nonce':
|
||||
api.settings.nonce[ 'customize-jetpack-simple-payments' ],
|
||||
customize_changeset_uuid: api.settings.changeset.uuid,
|
||||
params: {
|
||||
product_post_id: productPostId,
|
||||
post_title: widgetForm.find( '.jetpack-simple-payments-form-product-title' ).val(),
|
||||
post_content: widgetForm
|
||||
.find( '.jetpack-simple-payments-form-product-description' )
|
||||
.val(),
|
||||
image_id: widgetForm.find( '.jetpack-simple-payments-form-image-id' ).val(),
|
||||
currency: widgetForm.find( '.jetpack-simple-payments-form-product-currency' ).val(),
|
||||
price: widgetForm.find( '.jetpack-simple-payments-form-product-price' ).val(),
|
||||
multiple: widgetForm
|
||||
.find( '.jetpack-simple-payments-form-product-multiple' )
|
||||
.is( ':checked' )
|
||||
? 1
|
||||
: 0,
|
||||
email: widgetForm.find( '.jetpack-simple-payments-form-product-email' ).val(),
|
||||
},
|
||||
} );
|
||||
|
||||
request.done( function ( data ) {
|
||||
var select = widgetForm.find( 'select.jetpack-simple-payments-products' );
|
||||
var productOption = select.find( 'option[value="' + productPostId + '"]' );
|
||||
|
||||
if ( productOption.length > 0 ) {
|
||||
productOption.text( data.product_post_title );
|
||||
} else {
|
||||
select.append(
|
||||
$( '<option>', {
|
||||
value: data.product_post_id,
|
||||
text: data.product_post_title,
|
||||
} )
|
||||
);
|
||||
select.val( data.product_post_id ).change();
|
||||
}
|
||||
|
||||
widgetForm.find( '.jetpack-simple-payments-products-fieldset' ).show();
|
||||
widgetForm.find( '.jetpack-simple-payments-products-warning' ).hide();
|
||||
|
||||
changeFormAction( widgetForm, 'clear' );
|
||||
hideForm( widgetForm );
|
||||
} );
|
||||
|
||||
request.fail( function ( data ) {
|
||||
var validCodes = {
|
||||
post_title: 'product-title',
|
||||
price: 'product-price',
|
||||
email: 'product-email',
|
||||
};
|
||||
|
||||
data.forEach( function ( item ) {
|
||||
if ( Object.hasOwn( validCodes, item.code ) ) {
|
||||
widgetForm
|
||||
.find( '.jetpack-simple-payments-form-' + validCodes[ item.code ] )
|
||||
.addClass( 'invalid' );
|
||||
}
|
||||
} );
|
||||
|
||||
enableFormActions( widgetForm );
|
||||
} );
|
||||
};
|
||||
}
|
||||
|
||||
function deleteProduct( widgetForm ) {
|
||||
return function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
if ( ! confirm( jpSimplePaymentsStrings.deleteConfirmation ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var formProductId = parseInt(
|
||||
widgetForm.find( '.jetpack-simple-payments-form-product-id' ).val(),
|
||||
10
|
||||
);
|
||||
if ( ! formProductId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
disableFormActions( widgetForm );
|
||||
|
||||
widgetForm.find( '.spinner' ).show();
|
||||
|
||||
var request = wp.ajax.post( 'customize-jetpack-simple-payments-button-delete', {
|
||||
'customize-jetpack-simple-payments-nonce':
|
||||
api.settings.nonce[ 'customize-jetpack-simple-payments' ],
|
||||
customize_changeset_uuid: api.settings.changeset.uuid,
|
||||
params: {
|
||||
product_post_id: formProductId,
|
||||
},
|
||||
} );
|
||||
|
||||
request.done( function () {
|
||||
var productList = widgetForm.find( 'select.jetpack-simple-payments-products' )[ 0 ];
|
||||
productList.remove( productList.selectedIndex );
|
||||
productList.dispatchEvent( new Event( 'change' ) );
|
||||
|
||||
if ( $( productList ).has( 'option' ).length === 0 ) {
|
||||
//hide products select and label
|
||||
widgetForm.find( '.jetpack-simple-payments-products-fieldset' ).hide();
|
||||
//show empty products list warning
|
||||
widgetForm.find( '.jetpack-simple-payments-products-warning' ).show();
|
||||
}
|
||||
|
||||
changeFormAction( widgetForm, 'clear' );
|
||||
hideForm( widgetForm );
|
||||
} );
|
||||
};
|
||||
}
|
||||
} )( wp.customize, wp, jQuery );
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
/**
|
||||
* Display the Pay with PayPal Form.
|
||||
*
|
||||
* @html-template Jetpack_Simple_Payments_Widget::form
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
|
||||
<?php esc_html_e( 'Widget Title', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="widefat jetpack-simple-payments-widget-title"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</p>
|
||||
<p class="jetpack-simple-payments-products-fieldset"
|
||||
<?php
|
||||
if ( empty( $product_posts ) ) {
|
||||
echo 'style="display:none;"';
|
||||
}
|
||||
?>
|
||||
>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'product_post_id' ) ); ?>">
|
||||
<?php esc_html_e( 'Select a Pay with PayPal button:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<select
|
||||
class="widefat jetpack-simple-payments-products"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'product_post_id' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'product_post_id' ) ); ?>">
|
||||
<?php foreach ( $product_posts as $product_post ) { ?>
|
||||
<option value="<?php echo (int) $product_post->ID; ?>" <?php selected( (int) $instance['product_post_id'], $product_post->ID ); ?>>
|
||||
<?php echo esc_attr( get_the_title( $product_post ) ); ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</p>
|
||||
<?php if ( is_customize_preview() ) { ?>
|
||||
<p class="jetpack-simple-payments-products-warning"
|
||||
<?php
|
||||
if ( ! empty( $product_posts ) ) {
|
||||
echo 'style="display:none;"';
|
||||
}
|
||||
?>
|
||||
>
|
||||
<?php esc_html_e( "Looks like you don't have any products. You can create one using the Add New button below.", 'jetpack' ); ?>
|
||||
</p>
|
||||
<p>
|
||||
<div class="alignleft">
|
||||
<button class="button jetpack-simple-payments-edit-product" <?php disabled( empty( $product_posts ), true ); ?>>
|
||||
<?php esc_html_e( 'Edit Selected', 'jetpack' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="alignright">
|
||||
<button class="button jetpack-simple-payments-add-product"><?php esc_html_e( 'Add New', 'jetpack' ); ?></button>
|
||||
</div>
|
||||
<br class="clear">
|
||||
</p>
|
||||
<hr />
|
||||
<div class="jetpack-simple-payments-form" style="display: none;">
|
||||
<input
|
||||
type="hidden"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_action' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_action' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_action'] ); ?>"
|
||||
class="jetpack-simple-payments-form-action" />
|
||||
<input
|
||||
type="hidden"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_id' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_id' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_product_id'] ); ?>"
|
||||
class="jetpack-simple-payments-form-product-id" />
|
||||
<input
|
||||
type="hidden"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_image_id' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_image_id' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_product_image_id'] ); ?>"
|
||||
class="jetpack-simple-payments-form-image-id" />
|
||||
<input
|
||||
type="hidden"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_image_src' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_image_src' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_product_image_src'] ); ?>"
|
||||
class="jetpack-simple-payments-form-image-src" />
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_product_title' ) ); ?>">
|
||||
<?php esc_html_e( 'What is this payment for?', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="widefat field-title jetpack-simple-payments-form-product-title"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_title' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_title' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_product_title'] ); ?>" />
|
||||
<br />
|
||||
<small>
|
||||
<?php esc_html_e( 'For example: event tickets, charitable donations, training courses, coaching fees, etc.', 'jetpack' ); ?>
|
||||
</small>
|
||||
</p>
|
||||
<div class="jetpack-simple-payments-image-fieldset">
|
||||
<label><?php esc_html_e( 'Product image', 'jetpack' ); ?></label>
|
||||
<div class="placeholder"
|
||||
<?php
|
||||
if ( ! empty( $instance['form_product_image_id'] ) ) {
|
||||
echo 'style="display:none;"';
|
||||
}
|
||||
?>
|
||||
>
|
||||
<?php esc_html_e( 'Select an image', 'jetpack' ); ?>
|
||||
</div>
|
||||
<div class="jetpack-simple-payments-image"
|
||||
<?php
|
||||
if ( empty( $instance['form_product_image_id'] ) ) {
|
||||
echo 'style="display:none;"';
|
||||
}
|
||||
?>
|
||||
>
|
||||
<img src="<?php echo esc_url( $instance['form_product_image_src'] ); ?>" />
|
||||
<button class="button jetpack-simple-payments-remove-image"><?php esc_html_e( 'Remove image', 'jetpack' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_product_description' ) ); ?>">
|
||||
<?php esc_html_e( 'Description', 'jetpack' ); ?>
|
||||
</label>
|
||||
<textarea
|
||||
class="field-description widefat jetpack-simple-payments-form-product-description"
|
||||
rows=5
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_description' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_description' ) ); ?>"><?php echo esc_textarea( $instance['form_product_description'] ); ?></textarea>
|
||||
</p>
|
||||
<p class="cost">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_product_price' ) ); ?>">
|
||||
<?php esc_html_e( 'Price', 'jetpack' ); ?>
|
||||
</label>
|
||||
<select
|
||||
class="field-currency widefat jetpack-simple-payments-form-product-currency"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_currency' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_currency' ) ); ?>">
|
||||
<?php
|
||||
foreach ( Jetpack_Simple_Payments_Widget::$supported_currency_list as $code => $currency ) {
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $code ); ?>"<?php selected( $instance['form_product_currency'], $code ); ?>>
|
||||
<?php echo esc_html( "$code $currency" ); ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="field-price widefat jetpack-simple-payments-form-product-price"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_price' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_price' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['form_product_price'] ); ?>"
|
||||
placeholder="1.00" />
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
class="field-multiple jetpack-simple-payments-form-product-multiple"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_multiple' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_multiple' ) ); ?>"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
<?php checked( $instance['form_product_multiple'], '1' ); ?> />
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_product_multiple' ) ); ?>">
|
||||
<?php esc_html_e( 'Allow people to buy more than one item at a time.', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_product_email' ) ); ?>">
|
||||
<?php esc_html_e( 'Email', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="field-email widefat jetpack-simple-payments-form-product-email"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'form_product_email' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'form_product_email' ) ); ?>"
|
||||
type="email"
|
||||
value="<?php echo esc_attr( $instance['form_product_email'] ); ?>" />
|
||||
<small>
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* Translators: placeholders are a link to Paypal website and a target attribute. */
|
||||
__( 'This is where PayPal will send your money. To claim a payment, you\'ll need a <a href="%1$s" %2$s>PayPal account</a> connected to a bank account.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
'https://paypal.com',
|
||||
'target="_blank"'
|
||||
);
|
||||
?>
|
||||
</small>
|
||||
</p>
|
||||
<p>
|
||||
<div class="alignleft">
|
||||
<button type="button" class="button-link button-link-delete jetpack-simple-payments-delete-product">
|
||||
<?php esc_html_e( 'Delete Product', 'jetpack' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="alignright">
|
||||
<button name="<?php echo esc_attr( $this->get_field_name( 'save' ) ); ?>" class="button jetpack-simple-payments-save-product"><?php esc_html_e( 'Save', 'jetpack' ); ?></button>
|
||||
<span> | <button type="button" class="button-link jetpack-simple-payments-cancel-form"><?php esc_html_e( 'Cancel', 'jetpack' ); ?></button></span>
|
||||
</div>
|
||||
<br class="clear">
|
||||
</p>
|
||||
<hr />
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<p class="jetpack-simple-payments-products-warning">
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* Translators: placeholder is a link to the customizer. */
|
||||
__( 'This widget adds a payment button of your choice to your sidebar. To create or edit the payment buttons themselves, <a href="%s">use the Customizer</a>.', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( add_query_arg( array( 'autofocus[panel]' => 'widgets' ), admin_url( 'customize.php' ) ) )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php } ?>
|
||||
@@ -0,0 +1,8 @@
|
||||
@media screen and (min-width: 400px) {
|
||||
.widget.jetpack-simple-payments .jetpack-simple-payments-product {
|
||||
flex-direction: column;
|
||||
}
|
||||
.widget.jetpack-simple-payments .jetpack-simple-payments-details {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Display the Pay with PayPal Widget.
|
||||
*
|
||||
* @html-template Jetpack_Simple_Payments_Widget::widget
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
|
||||
|
||||
?>
|
||||
<div class='jetpack-simple-payments-wrapper'>
|
||||
<div class='jetpack-simple-payments-product'>
|
||||
<div class='jetpack-simple-payments-product-image'
|
||||
<?php
|
||||
if ( empty( $instance['form_product_image_id'] ) ) {
|
||||
echo 'style="display:none;"';
|
||||
}
|
||||
?>
|
||||
>
|
||||
<div class='jetpack-simple-payments-image'>
|
||||
<?php echo wp_get_attachment_image( (int) $instance['form_product_image_id'], 'full' ); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='jetpack-simple-payments-details'>
|
||||
<div class='jetpack-simple-payments-title'><p><?php echo esc_html( $instance['form_product_title'] ); ?></p></div>
|
||||
<div class='jetpack-simple-payments-description'><p><?php echo esc_html( $instance['form_product_description'] ); ?></p></div>
|
||||
<div class='jetpack-simple-payments-price'><p><?php echo esc_html( $instance['form_product_price'] ); ?> <?php echo esc_html( $instance['form_product_currency'] ); ?></p></div>
|
||||
<div class='jetpack-simple-payments-purchase-box'>
|
||||
<?php if ( $instance['form_product_multiple'] ) { ?>
|
||||
<div class='jetpack-simple-payments-items'>
|
||||
<input
|
||||
type='number'
|
||||
class='jetpack-simple-payments-items-number'
|
||||
value='1'
|
||||
min='1' />
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,832 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* Social Icons Widget.
|
||||
*/
|
||||
class Jetpack_Widget_Social_Icons extends WP_Widget {
|
||||
|
||||
const ID_BASE = 'jetpack_widget_social_icons';
|
||||
|
||||
/**
|
||||
* Default widget options.
|
||||
*
|
||||
* @var array Default widget options.
|
||||
*/
|
||||
protected $defaults;
|
||||
|
||||
/**
|
||||
* Widget constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
global $pagenow;
|
||||
|
||||
$widget_ops = array(
|
||||
'classname' => 'jetpack_widget_social_icons',
|
||||
'description' => __( 'Add social-media icons to your site.', 'jetpack' ),
|
||||
'show_instance_in_rest' => true,
|
||||
'customize_selective_refresh' => true,
|
||||
);
|
||||
|
||||
parent::__construct(
|
||||
'jetpack_widget_social_icons',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Social Icons', 'jetpack' ) ),
|
||||
$widget_ops
|
||||
);
|
||||
|
||||
$this->defaults = array(
|
||||
'title' => __( 'Follow Us', 'jetpack' ),
|
||||
'icon-size' => 'medium',
|
||||
'new-tab' => false,
|
||||
'icons' => array(
|
||||
array(
|
||||
'url' => '',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Enqueue admin scrips and styles, only in the customizer or the old widgets page.
|
||||
if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
add_action( 'admin_print_footer_scripts', array( $this, 'render_admin_js' ) );
|
||||
}
|
||||
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Social Icons" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = self::ID_BASE;
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Script & styles for admin widget form.
|
||||
*/
|
||||
public function enqueue_admin_scripts() {
|
||||
wp_enqueue_script(
|
||||
'jetpack-widget-social-icons-script',
|
||||
plugins_url( 'social-icons/social-icons-admin.js', __FILE__ ),
|
||||
array( 'jquery', 'jquery-ui-sortable' ),
|
||||
'20170506',
|
||||
true
|
||||
);
|
||||
wp_enqueue_style(
|
||||
'jetpack-widget-social-icons-admin',
|
||||
plugins_url( 'social-icons/social-icons-admin.css', __FILE__ ),
|
||||
array(),
|
||||
'20170506'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for front-end widget.
|
||||
*/
|
||||
public function enqueue_icon_scripts() {
|
||||
wp_enqueue_style( 'jetpack-widget-social-icons-styles', plugins_url( 'social-icons/social-icons.css', __FILE__ ), array(), '20170506' );
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript for admin widget form.
|
||||
*/
|
||||
public function render_admin_js() {
|
||||
?>
|
||||
<script type="text/html" id="tmpl-jetpack-widget-social-icons-template">
|
||||
<?php self::render_icons_template(); ?>
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Add SVG definitions to the footer.
|
||||
*/
|
||||
public function include_svg_icons() {
|
||||
// Define SVG sprite file in Jetpack.
|
||||
$svg_icons = dirname( __DIR__ ) . '/theme-tools/social-menu/social-menu.svg';
|
||||
$svg_icons = class_exists( 'Automattic\Jetpack\Classic_Theme_Helper\Main' ) ? JETPACK__PLUGIN_DIR . 'jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.svg' : dirname( __DIR__ ) . '/theme-tools/social-menu/social-menu.svg';
|
||||
// Define SVG sprite file in WPCOM.
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$svg_icons = class_exists( 'Automattic\Jetpack\Classic_Theme_Helper\Main' ) ? JETPACK__PLUGIN_DIR . 'jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.svg' : dirname( __DIR__ ) . '/social-menu/social-menu.svg';
|
||||
}
|
||||
|
||||
// If it exists, include it.
|
||||
if ( is_file( $svg_icons ) ) {
|
||||
$svg_contents = file_get_contents( $svg_icons ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Only reading a local file.
|
||||
}
|
||||
|
||||
if ( ! empty( $svg_contents ) ) {
|
||||
$allowed_tags = array(
|
||||
'svg' => array(
|
||||
'style' => true,
|
||||
'version' => true,
|
||||
'xmlns' => true,
|
||||
'xmlns:xlink' => true,
|
||||
),
|
||||
'defs' => array(),
|
||||
'symbol' => array(
|
||||
'id' => true,
|
||||
'viewbox' => true,
|
||||
),
|
||||
'path' => array(
|
||||
'd' => true,
|
||||
'style' => true,
|
||||
),
|
||||
);
|
||||
echo wp_kses( $svg_contents, $allowed_tags );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_icon_scripts();
|
||||
add_action( 'wp_footer', array( $this, 'include_svg_icons' ), 9999 );
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( ! empty( $instance['icons'] ) ) :
|
||||
|
||||
// Get supported social icons.
|
||||
$social_icons = $this->get_supported_icons();
|
||||
$default_icon = $this->get_svg_icon( array( 'icon' => 'chain' ) );
|
||||
|
||||
?>
|
||||
|
||||
<ul class="jetpack-social-widget-list size-<?php echo esc_attr( $instance['icon-size'] ); ?>">
|
||||
|
||||
<?php foreach ( $instance['icons'] as $icon ) : ?>
|
||||
|
||||
<?php if ( ! empty( $icon['url'] ) ) : ?>
|
||||
<li class="jetpack-social-widget-item">
|
||||
<?php
|
||||
printf(
|
||||
'<a href="%1$s" %2$s>',
|
||||
esc_url( $icon['url'], array( 'http', 'https', 'mailto', 'skype', 'sms' ) ),
|
||||
true === $instance['new-tab'] ?
|
||||
'target="_blank" rel="noopener noreferrer"' :
|
||||
'target="_self"'
|
||||
);
|
||||
|
||||
$found_icon = false;
|
||||
|
||||
foreach ( $social_icons as $social_icon ) {
|
||||
foreach ( $social_icon['url'] as $url_fragment ) {
|
||||
/*
|
||||
* url_fragment can be a URL host, or a regex, starting with #.
|
||||
* Let's check for both scenarios.
|
||||
*/
|
||||
if (
|
||||
// First Regex.
|
||||
(
|
||||
str_starts_with( $url_fragment, '#' ) && str_ends_with( $url_fragment, '#' )
|
||||
&& preg_match( $url_fragment, $icon['url'] )
|
||||
)
|
||||
// Then, regular host name.
|
||||
|| str_contains( $icon['url'], $url_fragment )
|
||||
) {
|
||||
printf(
|
||||
'<span class="screen-reader-text">%1$s</span>%2$s',
|
||||
esc_attr( $social_icon['label'] ),
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$this->get_svg_icon(
|
||||
array(
|
||||
'icon' => esc_attr( $social_icon['icon'] ),
|
||||
)
|
||||
)
|
||||
);
|
||||
$found_icon = true;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $found_icon ) {
|
||||
echo $default_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
</ul>
|
||||
|
||||
<?php
|
||||
endif;
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'social_icons' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
$instance['title'] = sanitize_text_field( $new_instance['title'] );
|
||||
$instance['icon-size'] = $this->defaults['icon-size'];
|
||||
$instance['url-icons'] = array_key_exists( 'url-icons', $new_instance ) ? $new_instance['url-icons'] : array();
|
||||
|
||||
if ( in_array( $new_instance['icon-size'], array( 'small', 'medium', 'large' ), true ) ) {
|
||||
$instance['icon-size'] = $new_instance['icon-size'];
|
||||
}
|
||||
|
||||
$instance['new-tab'] = isset( $new_instance['new-tab'] ) ? (bool) $new_instance['new-tab'] : false;
|
||||
$instance['icons'] = array();
|
||||
|
||||
if ( array_key_exists( 'url-icons', $new_instance ) ) {
|
||||
foreach ( $new_instance['url-icons'] as $url ) {
|
||||
$url = filter_var( $url, FILTER_SANITIZE_URL );
|
||||
|
||||
if ( ! empty( $url ) ) {
|
||||
$instance['icons'][] = array(
|
||||
'url' => $url,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->defaults );
|
||||
$title = sanitize_text_field( $instance['title'] );
|
||||
$sizes = array(
|
||||
'small' => __( 'Small', 'jetpack' ),
|
||||
'medium' => __( 'Medium', 'jetpack' ),
|
||||
'large' => __( 'Large', 'jetpack' ),
|
||||
);
|
||||
$new_tab = isset( $instance['new-tab'] ) ? (bool) $instance['new-tab'] : false;
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'icon-size' ) ); ?>"><?php esc_html_e( 'Size:', 'jetpack' ); ?></label>
|
||||
<select class="widefat" name="<?php echo esc_attr( $this->get_field_name( 'icon-size' ) ); ?>">
|
||||
<?php foreach ( $sizes as $value => $label ) : ?>
|
||||
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $instance['icon-size'] ); ?>><?php echo esc_attr( $label ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<div class="jetpack-social-icons-widget-list"
|
||||
data-url-icon-id="<?php echo esc_attr( $this->get_field_id( 'url-icons' ) ); ?>"
|
||||
data-url-icon-name="<?php echo esc_attr( $this->get_field_name( 'url-icons' ) ); ?>"
|
||||
>
|
||||
|
||||
<?php
|
||||
foreach ( $instance['icons'] as $icon ) {
|
||||
self::render_icons_template(
|
||||
array(
|
||||
'url-icon-id' => $this->get_field_id( 'url-icons' ),
|
||||
'url-icon-name' => $this->get_field_name( 'url-icons' ),
|
||||
'url-value' => $icon['url'],
|
||||
)
|
||||
);
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="jetpack-social-icons-widget add-button">
|
||||
<button type="button" class="button jetpack-social-icons-add-button">
|
||||
<?php esc_html_e( 'Add an icon', 'jetpack' ); ?>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
switch ( get_locale() ) {
|
||||
case 'es':
|
||||
$support = 'https://es.support.wordpress.com/social-media-icons-widget/#iconos-disponibles';
|
||||
break;
|
||||
|
||||
case 'pt-br':
|
||||
$support = 'https://br.support.wordpress.com/widgets/widget-de-icones-sociais/#ícones-disponíveis';
|
||||
break;
|
||||
|
||||
default:
|
||||
$support = 'https://en.support.wordpress.com/widgets/social-media-icons-widget/#available-icons';
|
||||
}
|
||||
?>
|
||||
|
||||
<p>
|
||||
<em><a href="<?php echo esc_url( $support ); ?>" target="_blank" rel="noopener noreferrer">
|
||||
<?php esc_html_e( 'View available icons', 'jetpack' ); ?>
|
||||
</a></em>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="checkbox" class="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'new-tab' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'new-tab' ) ); ?>" <?php checked( $new_tab ); ?> />
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'new-tab' ) ); ?>"><?php esc_html_e( 'Open link in a new tab', 'jetpack' ); ?></label>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates template to add icons.
|
||||
*
|
||||
* @param array $args Template arguments.
|
||||
*/
|
||||
private static function render_icons_template( $args = array() ) {
|
||||
$defaults = array(
|
||||
'url-icon-id' => '',
|
||||
'url-icon-name' => '',
|
||||
'url-value' => '',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
?>
|
||||
|
||||
<div class="jetpack-social-icons-widget-item">
|
||||
<div class="jetpack-social-icons-widget-item-wrapper">
|
||||
<div class="handle"></div>
|
||||
|
||||
<p class="jetpack-widget-social-icons-url">
|
||||
<?php
|
||||
printf(
|
||||
'<input class="widefat" id="%1$s" name="%2$s[]" type="text" placeholder="%3$s" value="%4$s"/>',
|
||||
esc_attr( $args['url-icon-id'] ),
|
||||
esc_attr( $args['url-icon-name'] ),
|
||||
esc_attr__( 'Account URL', 'jetpack' ),
|
||||
esc_url( $args['url-value'], array( 'http', 'https', 'mailto', 'skype', 'sms' ) )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p class="jetpack-widget-social-icons-remove-item">
|
||||
<a class="jetpack-widget-social-icons-remove-item-button" href="javascript:;">
|
||||
<?php esc_html_e( 'Remove', 'jetpack' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SVG markup.
|
||||
*
|
||||
* @param array $args {
|
||||
* Parameters needed to display an SVG.
|
||||
*
|
||||
* @type string $icon Required SVG icon filename.
|
||||
* }
|
||||
* @return string SVG markup.
|
||||
*/
|
||||
public function get_svg_icon( $args = array() ) {
|
||||
// Make sure $args are an array.
|
||||
if ( empty( $args ) ) {
|
||||
return esc_html__( 'Please define default parameters in the form of an array.', 'jetpack' );
|
||||
}
|
||||
|
||||
// Set defaults.
|
||||
$defaults = array(
|
||||
'icon' => '',
|
||||
);
|
||||
|
||||
// Parse args.
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// Define an icon.
|
||||
if ( false === array_key_exists( 'icon', $args ) ) {
|
||||
return esc_html__( 'Please define an SVG icon filename.', 'jetpack' );
|
||||
}
|
||||
|
||||
// Set aria hidden.
|
||||
$aria_hidden = ' aria-hidden="true"';
|
||||
|
||||
// Begin SVG markup.
|
||||
$svg = '<svg class="icon icon-' . esc_attr( $args['icon'] ) . '"' . $aria_hidden . ' role="presentation">';
|
||||
|
||||
/*
|
||||
* Display the icon.
|
||||
*
|
||||
* The whitespace around `<use>` is intentional - it is a work around to a keyboard navigation bug in Safari 10.
|
||||
*
|
||||
* See https://core.trac.wordpress.org/ticket/38387.
|
||||
*/
|
||||
$svg .= ' <use href="#icon-' . esc_html( $args['icon'] ) . '" xlink:href="#icon-' . esc_html( $args['icon'] ) . '"></use> ';
|
||||
|
||||
$svg .= '</svg>';
|
||||
|
||||
return $svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of supported social links (URL, icon, and label).
|
||||
*
|
||||
* @return array $social_links_icons
|
||||
*/
|
||||
public function get_supported_icons() {
|
||||
$social_links_icons = array(
|
||||
array(
|
||||
'url' => array( '500px.com' ),
|
||||
'icon' => '500px',
|
||||
'label' => '500px',
|
||||
),
|
||||
array(
|
||||
'url' => array(
|
||||
'#https?:\/\/(www\.)?amazon\.(com|cn|in|fr|de|it|nl|es|co|ca)\/#',
|
||||
),
|
||||
'icon' => 'amazon',
|
||||
'label' => 'Amazon',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'apple.com' ),
|
||||
'icon' => 'apple',
|
||||
'label' => 'Apple',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'itunes.com' ),
|
||||
'icon' => 'apple',
|
||||
'label' => 'iTunes',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'bandcamp.com' ),
|
||||
'icon' => 'bandcamp',
|
||||
'label' => 'Bandcamp',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'behance.net' ),
|
||||
'icon' => 'behance',
|
||||
'label' => 'Behance',
|
||||
),
|
||||
array(
|
||||
'url' => array(
|
||||
'blogger.com',
|
||||
'blogspot.com',
|
||||
),
|
||||
'icon' => 'blogger',
|
||||
'label' => 'Blogger',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'bsky.app' ),
|
||||
'icon' => 'bluesky',
|
||||
'label' => 'Bluesky',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'codepen.io' ),
|
||||
'icon' => 'codepen',
|
||||
'label' => 'CodePen',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'deviantart.com' ),
|
||||
'icon' => 'deviantart',
|
||||
'label' => 'DeviantArt',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'digg.com' ),
|
||||
'icon' => 'digg',
|
||||
'label' => 'Digg',
|
||||
),
|
||||
array(
|
||||
'url' => array( '#discord\.gg|discordapp\.com#' ),
|
||||
'icon' => 'discord',
|
||||
'label' => 'Discord',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'dribbble.com' ),
|
||||
'icon' => 'dribbble',
|
||||
'label' => 'Dribbble',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'dropbox.com' ),
|
||||
'icon' => 'dropbox',
|
||||
'label' => 'Dropbox',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'etsy.com' ),
|
||||
'icon' => 'etsy',
|
||||
'label' => 'Etsy',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'eventbrite.com' ),
|
||||
'icon' => 'eventbrite',
|
||||
'label' => 'Eventbrite',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'facebook.com' ),
|
||||
'icon' => 'facebook',
|
||||
'label' => 'Facebook',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'flickr.com' ),
|
||||
'icon' => 'flickr',
|
||||
'label' => 'Flickr',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'foursquare.com' ),
|
||||
'icon' => 'foursquare',
|
||||
'label' => 'Foursquare',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'ghost.org' ),
|
||||
'icon' => 'ghost',
|
||||
'label' => 'Ghost',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'goodreads.com' ),
|
||||
'icon' => 'goodreads',
|
||||
'label' => 'Goodreads',
|
||||
),
|
||||
array(
|
||||
'url' => array( '#google\.(com|co\.uk|ca|cn|it)#' ),
|
||||
'icon' => 'google',
|
||||
'label' => 'Google',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'github.com' ),
|
||||
'icon' => 'github',
|
||||
'label' => 'GitHub',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'instagram.com' ),
|
||||
'icon' => 'instagram',
|
||||
'label' => 'Instagram',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'linkedin.com' ),
|
||||
'icon' => 'linkedin',
|
||||
'label' => 'LinkedIn',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'mailto:' ),
|
||||
'icon' => 'mail',
|
||||
'label' => __( 'Email', 'jetpack' ),
|
||||
),
|
||||
array(
|
||||
'url' => jetpack_mastodon_get_instance_list(),
|
||||
'icon' => 'mastodon',
|
||||
'label' => 'Mastodon',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'meetup.com' ),
|
||||
'icon' => 'meetup',
|
||||
'label' => 'Meetup',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'medium.com' ),
|
||||
'icon' => 'medium',
|
||||
'label' => 'Medium',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'nextdoor.com' ),
|
||||
'icon' => 'nextdoor',
|
||||
'label' => 'Nextdoor',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'patreon.com' ),
|
||||
'icon' => 'patreon',
|
||||
'label' => 'Patreon',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'pinterest.' ),
|
||||
'icon' => 'pinterest',
|
||||
'label' => 'Pinterest',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'getpocket.com' ),
|
||||
'icon' => 'pocket',
|
||||
'label' => 'Pocket',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'ravelry.com' ),
|
||||
'icon' => 'ravelry',
|
||||
'label' => 'Ravelry',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'reddit.com' ),
|
||||
'icon' => 'reddit',
|
||||
'label' => 'Reddit',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'skype.com' ),
|
||||
'icon' => 'skype',
|
||||
'label' => 'Skype',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'skype:' ),
|
||||
'icon' => 'skype',
|
||||
'label' => 'Skype',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'slideshare.net' ),
|
||||
'icon' => 'slideshare',
|
||||
'label' => 'SlideShare',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'sms:' ),
|
||||
'icon' => 'sms',
|
||||
'label' => 'SMS',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'snapchat.com' ),
|
||||
'icon' => 'snapchat',
|
||||
'label' => 'Snapchat',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'soundcloud.com' ),
|
||||
'icon' => 'soundcloud',
|
||||
'label' => 'SoundCloud',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'spotify.com' ),
|
||||
'icon' => 'spotify',
|
||||
'label' => 'Spotify',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'stackoverflow.com' ),
|
||||
'icon' => 'stackoverflow',
|
||||
'label' => 'Stack Overflow',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'strava.com' ),
|
||||
'icon' => 'strava',
|
||||
'label' => 'Strava',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'stumbleupon.com' ),
|
||||
'icon' => 'stumbleupon',
|
||||
'label' => 'StumbleUpon',
|
||||
),
|
||||
array(
|
||||
'url' => array( '#https?:\/\/(www\.)?(telegram|t)\.me#' ),
|
||||
'icon' => 'telegram',
|
||||
'label' => 'Telegram',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'threads.net' ),
|
||||
'icon' => 'threads',
|
||||
'label' => 'Threads',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'tiktok.com' ),
|
||||
'icon' => 'tiktok',
|
||||
'label' => 'TikTok',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'tumblr.com' ),
|
||||
'icon' => 'tumblr',
|
||||
'label' => 'Tumblr',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'twitch.tv' ),
|
||||
'icon' => 'twitch',
|
||||
'label' => 'Twitch',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'twitter.com' ),
|
||||
'icon' => 'twitter',
|
||||
'label' => 'Twitter',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'vimeo.com' ),
|
||||
'icon' => 'vimeo',
|
||||
'label' => 'Vimeo',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'vk.com' ),
|
||||
'icon' => 'vk',
|
||||
'label' => 'VK',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'whatsapp.com' ),
|
||||
'icon' => 'whatsapp',
|
||||
'label' => 'WhatsApp',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'woocommerce.com' ),
|
||||
'icon' => 'woocommerce',
|
||||
'label' => 'WooCommerce',
|
||||
),
|
||||
array(
|
||||
'url' => array( '#wordpress\.(com|org)#' ),
|
||||
'icon' => 'wordpress',
|
||||
'label' => 'WordPress',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'yelp.com' ),
|
||||
'icon' => 'yelp',
|
||||
'label' => 'Yelp',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'x.com' ),
|
||||
'icon' => 'x',
|
||||
'label' => 'X',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'xanga.com' ),
|
||||
'icon' => 'xanga',
|
||||
'label' => 'Xanga',
|
||||
),
|
||||
array(
|
||||
'url' => array( 'youtube.com' ),
|
||||
'icon' => 'youtube',
|
||||
'label' => 'YouTube',
|
||||
),
|
||||
|
||||
// keep feed at the end so that more specific icons can take precedence.
|
||||
array(
|
||||
'url' => array(
|
||||
'/feed/', // WordPress default feed url.
|
||||
'/feeds/', // Blogspot and others.
|
||||
'/blog/feed', // No trailing slash WordPress feed, could use /feed but may match unexpectedly.
|
||||
'format=RSS', // Squarespace and others.
|
||||
'/rss', // Tumblr.
|
||||
'/.rss', // Reddit.
|
||||
'/rss.xml', // Moveable Type, Typepad.
|
||||
'http://rss.', // Old custom format.
|
||||
'https://rss.', // Old custom format.
|
||||
'rss=1',
|
||||
'/feed=rss', // Catches feed=rss / feed=rss2.
|
||||
'?feed=rss', // WordPress non-permalink - Catches feed=rss / feed=rss2.
|
||||
'?feed=rdf', // WordPress non-permalink.
|
||||
'?feed=atom', // WordPress non-permalink.
|
||||
'http://feeds.', // FeedBurner.
|
||||
'https://feeds.', // FeedBurner.
|
||||
'/feed.xml', // Feedburner Alias, and others.
|
||||
'/index.xml', // Moveable Type, and others.
|
||||
'/atom.xml', // Typepad, Squarespace.
|
||||
'.atom', // Shopify blog.
|
||||
'/atom', // Some non-WordPress feeds.
|
||||
'index.rdf', // Typepad.
|
||||
),
|
||||
'icon' => 'feed',
|
||||
'label' => __( 'RSS Feed', 'jetpack' ),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the list of services matching Social Media Icons available in the Social Icons SVG sprite.
|
||||
*
|
||||
* @since 12.3
|
||||
*
|
||||
* @param array $social_links_icons Array of social links icons.
|
||||
*/
|
||||
return apply_filters(
|
||||
'jetpack_social_icons_supported_icons',
|
||||
$social_links_icons
|
||||
);
|
||||
}
|
||||
} // Jetpack_Widget_Social_Icons
|
||||
|
||||
/**
|
||||
* Register and load the widget.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function jetpack_widget_social_icons_load() {
|
||||
register_widget( 'Jetpack_Widget_Social_Icons' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_widget_social_icons_load' );
|
||||
@@ -0,0 +1,94 @@
|
||||
.jetpack-social-icons-widget-item {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
cursor: move;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html[class*='wordpress_com'] .jetpack-social-icons-widget-item,
|
||||
.in-calypso .jetpack-social-icons-widget-item {
|
||||
border-color: #c8d7e1;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item:hover {
|
||||
outline: 1px solid #999;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
html[class*='wordpress_com'] .jetpack-social-icons-widget-item:hover,
|
||||
.in-calypso .jetpack-social-icons-widget-item:hover {
|
||||
outline-color: #a8bece;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item.ui-sortable-helper {
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
html[class*='wordpress_com'] .jetpack-social-icons-widget-item.ui-sortable-helper,
|
||||
.in-calypso .jetpack-social-icons-widget-item.ui-sortable-helper {
|
||||
border-color: #a8bece;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item.ui-sortable-helper {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item.ui-state-placeholder {
|
||||
border-color: #a7aaad;
|
||||
border-style: dashed;
|
||||
margin: 0 0 1px;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item + .jetpack-social-icons-widget-item:not(.ui-state-placeholder) {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item-wrapper {
|
||||
padding: 1em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item .handle {
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item p {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-widget-item p + p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.jetpack-widget-social-icons-remove-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jetpack-widget-social-icons-remove-item-button {
|
||||
color: #d63638;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.jetpack-widget-social-icons-remove-item-button:focus,
|
||||
.jetpack-widget-social-icons-remove-item-button:hover {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.jetpack-social-icons-add-button:before {
|
||||
content: "\f132";
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: -2px;
|
||||
top: -1px;
|
||||
font: 400 20px/1 dashicons;
|
||||
vertical-align: middle;
|
||||
transition: all 0.2s;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
( function ( $ ) {
|
||||
var timeout = null;
|
||||
|
||||
// Make the list of items sortable.
|
||||
function initWidget( widget ) {
|
||||
widget.find( '.jetpack-social-icons-widget-list' ).sortable( {
|
||||
items: '> .jetpack-social-icons-widget-item',
|
||||
handle: '.handle',
|
||||
cursor: 'move',
|
||||
placeholder: 'jetpack-social-icons-widget-item ui-state-placeholder',
|
||||
containment: widget,
|
||||
forcePlaceholderSize: true,
|
||||
update: function () {
|
||||
livePreviewUpdate( $( this ).parents( '.form' ).find( '.widget-control-save' ) );
|
||||
},
|
||||
} );
|
||||
}
|
||||
|
||||
// Live preview update.
|
||||
function livePreviewUpdate( button ) {
|
||||
if ( ! $( document.body ).hasClass( 'wp-customizer' ) || ! button.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.trigger( 'click' ).hide();
|
||||
}
|
||||
|
||||
$( document ).ready( function () {
|
||||
// Add an item.
|
||||
$( document ).on(
|
||||
'click',
|
||||
'.jetpack-social-icons-widget.add-button button',
|
||||
function ( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
var template, widgetContent, widgetList, widgetLastItem, urlId, urlName;
|
||||
|
||||
template = $( $.trim( $( '#tmpl-jetpack-widget-social-icons-template' ).html() ) );
|
||||
widgetContent = $( this ).parents( '.widget-content' );
|
||||
widgetList = widgetContent.find( '.jetpack-social-icons-widget-list' );
|
||||
urlId = widgetList.data( 'url-icon-id' );
|
||||
urlName = widgetList.data( 'url-icon-name' );
|
||||
|
||||
template
|
||||
.find( '.jetpack-widget-social-icons-url input' )
|
||||
.attr( 'id', urlId )
|
||||
.attr( 'name', urlName + '[]' );
|
||||
|
||||
widgetList.append( template );
|
||||
|
||||
widgetLastItem = widgetContent.find( '.jetpack-social-icons-widget-item:last' );
|
||||
widgetLastItem.find( 'input:first' ).trigger( 'focus' );
|
||||
}
|
||||
);
|
||||
|
||||
// Remove an item.
|
||||
$( document ).on(
|
||||
'click',
|
||||
'.jetpack-widget-social-icons-remove-item-button',
|
||||
function ( event ) {
|
||||
event.preventDefault();
|
||||
var widgetItem = $( this ).parents( '.jetpack-social-icons-widget-item' );
|
||||
widgetItem.find( 'input' ).change();
|
||||
widgetItem.remove();
|
||||
}
|
||||
);
|
||||
|
||||
// Event handler for widget open button.
|
||||
$( document ).on(
|
||||
'click',
|
||||
'div.widget[id*="jetpack_widget_social_icons"] .widget-title, div.widget[id*="jetpack_widget_social_icons"] .widget-action',
|
||||
function () {
|
||||
if ( $( this ).parents( '#available-widgets' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
initWidget( $( this ).parents( '.widget[id*="jetpack_widget_social_icons"]' ) );
|
||||
}
|
||||
);
|
||||
|
||||
// Event handler for widget added.
|
||||
$( document ).on( 'widget-added', function ( event, widget ) {
|
||||
if ( widget.is( '[id*="jetpack_widget_social_icons"]' ) ) {
|
||||
event.preventDefault();
|
||||
initWidget( widget );
|
||||
}
|
||||
} );
|
||||
|
||||
// Event handler for widget updated.
|
||||
$( document ).on( 'widget-updated', function ( event, widget ) {
|
||||
if ( widget.is( '[id*="jetpack_widget_social_icons"]' ) ) {
|
||||
event.preventDefault();
|
||||
initWidget( widget );
|
||||
}
|
||||
} );
|
||||
|
||||
// Live preview update on input focus out.
|
||||
$( document ).on( 'focusout', 'input[name*="jetpack_widget_social_icons"]', function () {
|
||||
livePreviewUpdate( $( this ).parents( '.form' ).find( '.widget-control-save' ) );
|
||||
} );
|
||||
|
||||
// Live preview update on input enter key.
|
||||
$( document ).on( 'keydown', 'input[name*="jetpack_widget_social_icons"]', function ( event ) {
|
||||
if ( event.keyCode === 13 ) {
|
||||
livePreviewUpdate( $( this ).parents( '.form' ).find( '.widget-control-save' ) );
|
||||
}
|
||||
} );
|
||||
|
||||
// Live preview update on input key up 1s.
|
||||
$( document ).on( 'keyup', 'input[name*="jetpack_widget_social_icons"]', function () {
|
||||
clearTimeout( timeout );
|
||||
|
||||
timeout = setTimeout( function () {
|
||||
livePreviewUpdate( $( this ).parents( '.form' ).find( '.widget-control-save' ) );
|
||||
}, 1000 );
|
||||
} );
|
||||
|
||||
// Live preview update on select change.
|
||||
$( document ).on( 'change', 'select[name*="jetpack_widget_social_icons"]', function () {
|
||||
livePreviewUpdate( $( this ).parents( '.form' ).find( '.widget-control-save' ) );
|
||||
} );
|
||||
} );
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1,75 @@
|
||||
.jetpack_widget_social_icons ul,
|
||||
.jetpack_widget_social_icons li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons ul {
|
||||
display: block;
|
||||
margin: 0 0 1.5em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons ul li {
|
||||
border: 0;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons ul li:before,
|
||||
.jetpack_widget_social_icons ul li:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons a {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
display: block;
|
||||
height: 24px;
|
||||
text-decoration: none;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons svg {
|
||||
color: inherit;
|
||||
fill: currentColor;
|
||||
height: inherit;
|
||||
vertical-align: middle;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
/* Sizes */
|
||||
|
||||
.jetpack_widget_social_icons ul.size-small a {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons ul.size-medium a {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.jetpack_widget_social_icons ul.size-large a {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
/*
|
||||
Text meant only for screen readers.
|
||||
Provides support for themes that do not bundle this CSS yet.
|
||||
@see https://make.wordpress.org/accessibility/2015/02/09/hiding-text-for-screen-readers-with-wordpress-core/
|
||||
***********************************/
|
||||
.screen-reader-text {
|
||||
border: 0;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute ! important;
|
||||
width: 1px;
|
||||
word-wrap: normal ! important;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName.php
|
||||
/**
|
||||
* Social Media Icons Widget
|
||||
*
|
||||
* This widget is now deprecated.
|
||||
* Any new features should go into modules/widgets/social-icons.php instead.
|
||||
*
|
||||
* @see https://github.com/Automattic/jetpack/pull/8498
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* WPCOM_social_media_icons_widget class.
|
||||
*
|
||||
* @extends WP_Widget
|
||||
*
|
||||
* phpcs:disable PEAR.NamingConventions.ValidClassName.Invalid
|
||||
*/
|
||||
class WPCOM_social_media_icons_widget extends WP_Widget {
|
||||
// phpcs:enable PEAR.NamingConventions.ValidClassName.Invalid
|
||||
/**
|
||||
* Defaults
|
||||
*
|
||||
* @var mixed
|
||||
* @access private
|
||||
*/
|
||||
private $defaults;
|
||||
|
||||
/**
|
||||
* Services
|
||||
*
|
||||
* @var mixed
|
||||
* @access private
|
||||
*/
|
||||
private $services;
|
||||
|
||||
/**
|
||||
* __construct function.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'wpcom_social_media_icons_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Social Media Icons (Deprecated)', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'A simple widget that displays social media icons.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
$this->defaults = array(
|
||||
'title' => __( 'Social', 'jetpack' ),
|
||||
'facebook_username' => '',
|
||||
'twitter_username' => '',
|
||||
'instagram_username' => '',
|
||||
'pinterest_username' => '',
|
||||
'linkedin_username' => '',
|
||||
'github_username' => '',
|
||||
'youtube_username' => '',
|
||||
'vimeo_username' => '',
|
||||
'googleplus_username' => '',
|
||||
'flickr_username' => '',
|
||||
'wordpress_username' => '',
|
||||
'twitch_username' => '',
|
||||
'tumblr_username' => '',
|
||||
);
|
||||
$this->services = array(
|
||||
'facebook' => array( 'Facebook', 'https://www.facebook.com/%s/' ),
|
||||
'twitter' => array( 'Twitter', 'https://twitter.com/%s/' ),
|
||||
'instagram' => array( 'Instagram', 'https://www.instagram.com/%s/' ),
|
||||
'pinterest' => array( 'Pinterest', 'https://www.pinterest.com/%s/' ),
|
||||
'linkedin' => array( 'LinkedIn', 'https://www.linkedin.com/in/%s/' ),
|
||||
'github' => array( 'GitHub', 'https://github.com/%s/' ),
|
||||
'youtube' => array( 'YouTube', 'https://www.youtube.com/%s/' ),
|
||||
'vimeo' => array( 'Vimeo', 'https://vimeo.com/%s/' ),
|
||||
'googleplus' => array( 'Google+', 'https://plus.google.com/u/0/%s/' ),
|
||||
'flickr' => array( 'Flickr', 'https://www.flickr.com/photos/%s/' ),
|
||||
'wordpress' => array( 'WordPress.org', 'https://profiles.wordpress.org/%s/' ),
|
||||
'twitch' => array( 'Twitch', 'https://www.twitch.tv/%s/' ),
|
||||
'tumblr' => array( 'Tumblr', 'https://%s.tumblr.com' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Style.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_register_style( 'jetpack_social_media_icons_widget', plugins_url( 'social-media-icons/style.css', __FILE__ ), array(), '20150602' );
|
||||
wp_enqueue_style( 'jetpack_social_media_icons_widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Genericons.
|
||||
*
|
||||
* @access private
|
||||
* @return bool
|
||||
*/
|
||||
private function check_genericons() {
|
||||
global $wp_styles;
|
||||
foreach ( $wp_styles->queue as $handle ) {
|
||||
if ( false !== stristr( $handle, 'genericons' ) ) {
|
||||
return $handle;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget Front End.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $args Arguments.
|
||||
* @param mixed $instance Instance.
|
||||
* @return void
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults );
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$instance['title'] = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
||||
|
||||
/*
|
||||
* Enqueue frontend assets.
|
||||
*/
|
||||
|
||||
if ( ! $this->check_genericons() ) {
|
||||
wp_enqueue_style( 'genericons' );
|
||||
}
|
||||
|
||||
$this->enqueue_style();
|
||||
|
||||
$index = 10;
|
||||
$html = array();
|
||||
/* Translators: 1. Username. 2. Service name. */
|
||||
$alt_text = esc_attr__( 'View %1$s’s profile on %2$s', 'jetpack' );
|
||||
foreach ( $this->services as $service => $data ) {
|
||||
list( $service_name, $url ) = $data;
|
||||
if ( ! isset( $instance[ $service . '_username' ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$link_username = $instance[ $service . '_username' ];
|
||||
$username = $link_username;
|
||||
if ( empty( $username ) ) {
|
||||
continue;
|
||||
}
|
||||
$index += 10;
|
||||
$predefined_url = false;
|
||||
|
||||
/** Check if full URL entered in configuration, use it instead of tinkering */
|
||||
if (
|
||||
in_array(
|
||||
wp_parse_url( $username, PHP_URL_SCHEME ),
|
||||
array( 'http', 'https' ),
|
||||
true
|
||||
)
|
||||
) {
|
||||
$predefined_url = $username;
|
||||
|
||||
/*
|
||||
* In case of a predefined link we only display the service name
|
||||
* for screen readers
|
||||
*/
|
||||
$alt_text = '%2$s';
|
||||
}
|
||||
|
||||
if ( 'googleplus' === $service
|
||||
&& ! is_numeric( $username )
|
||||
&& ! str_starts_with( $username, '+' )
|
||||
) {
|
||||
$link_username = '+' . $username;
|
||||
}
|
||||
if ( 'youtube' === $service && str_starts_with( $username, 'UC' ) ) {
|
||||
$link_username = 'channel/' . $username;
|
||||
} elseif ( 'youtube' === $service ) {
|
||||
$link_username = 'user/' . $username;
|
||||
}
|
||||
|
||||
if ( ! $predefined_url ) {
|
||||
$predefined_url = sprintf( $url, $link_username );
|
||||
}
|
||||
/**
|
||||
* Fires for each profile link in the social icons widget. Can be used
|
||||
* to change the links for certain social networks if needed. All URLs
|
||||
* will be passed through `esc_attr` on output.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param string $url the currently processed URL
|
||||
* @param string $service the lowercase service slug, e.g. 'facebook', 'youtube', etc.
|
||||
*/
|
||||
$link = apply_filters(
|
||||
'jetpack_social_media_icons_widget_profile_link',
|
||||
$predefined_url,
|
||||
$service
|
||||
);
|
||||
$html[ $index ] = sprintf(
|
||||
'<a href="%1$s" class="genericon genericon-%2$s" target="_blank"><span class="screen-reader-text">%3$s</span></a>',
|
||||
esc_attr( $link ),
|
||||
esc_attr( $service ),
|
||||
sprintf( $alt_text, esc_html( $username ), $service_name )
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Fires at the end of the list of Social Media accounts.
|
||||
* Can be used to add a new Social Media Site to the Social Media Icons Widget.
|
||||
* The filter function passed the array of HTML entries that will be sorted
|
||||
* by key, each wrapped in a list item element and output as an unsorted list.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param array $html Associative array of HTML snippets per each icon.
|
||||
*/
|
||||
$html = apply_filters( 'jetpack_social_media_icons_widget_array', $html );
|
||||
ksort( $html );
|
||||
$html = '<ul><li>' . implode( '</li><li>', $html ) . '</li></ul>';
|
||||
if ( ! empty( $instance['title'] ) ) {
|
||||
$html = $args['before_title'] . $instance['title'] . $args['after_title'] . $html;
|
||||
}
|
||||
$html = $args['before_widget'] . $html . $args['after_widget'];
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'social_media_icons' );
|
||||
|
||||
/**
|
||||
* Filters the Social Media Icons widget output.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $html Social Media Icons widget html output.
|
||||
*/
|
||||
echo apply_filters( 'jetpack_social_media_icons_widget_output', $html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget Settings.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $instance Instance.
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults );
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_attr_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['title'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
<?php
|
||||
foreach ( $this->services as $service => $data ) {
|
||||
list( $service_name, $url ) = $data; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( $service . '_username' ) ); ?>">
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s is a social network name, e.g. Facebook. */
|
||||
esc_html__( '%s username:', 'jetpack' ),
|
||||
esc_html( $service_name )
|
||||
);
|
||||
?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( $service . '_username' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( $service . '_username' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance[ $service . '_username' ] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Widget Settings.
|
||||
*
|
||||
* @access public
|
||||
* @param array $new_instance New Instance.
|
||||
* @param array|null $old_instance Old Instance.
|
||||
* @return array Instance.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = (array) $old_instance;
|
||||
foreach ( $new_instance as $field => $value ) {
|
||||
$instance[ $field ] = sanitize_text_field( $new_instance[ $field ] );
|
||||
}
|
||||
// Stats.
|
||||
$stats = $instance;
|
||||
unset( $stats['title'] );
|
||||
$stats = array_filter( $stats );
|
||||
$stats = array_keys( $stats );
|
||||
$stats = array_map( array( $this, 'remove_username' ), $stats );
|
||||
foreach ( $stats as $val ) {
|
||||
/**
|
||||
* Fires for each Social Media account being saved in the Social Media Widget settings.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string social-media-links-widget-svcs Type of action to track.
|
||||
* @param string $val Name of the Social Media account being saved.
|
||||
*/
|
||||
do_action( 'jetpack_bump_stats_extras', 'social-media-links-widget-svcs', $val );
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove username from value before to save stats.
|
||||
*
|
||||
* @access public
|
||||
* @param string $val Value.
|
||||
* @return string Value.
|
||||
*/
|
||||
public function remove_username( $val ) {
|
||||
return str_replace( '_username', '', $val );
|
||||
}
|
||||
} // End Class.
|
||||
|
||||
/**
|
||||
* Register and load the widget.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function wpcom_social_media_icons_widget_load_widget() {
|
||||
$transient = 'wpcom_social_media_icons_widget::is_active';
|
||||
$has_widget = get_transient( $transient );
|
||||
|
||||
if ( false === $has_widget ) {
|
||||
$is_active_widget = is_active_widget( false, false, 'wpcom_social_media_icons_widget', false );
|
||||
$has_widget = (int) ! empty( $is_active_widget );
|
||||
set_transient( $transient, $has_widget, 1 * HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
// [DEPRECATION]: Only register widget if active widget exists already
|
||||
if ( $has_widget ) {
|
||||
register_widget( 'wpcom_social_media_icons_widget' );
|
||||
}
|
||||
}
|
||||
add_action( 'widgets_init', 'wpcom_social_media_icons_widget_load_widget' );
|
||||
@@ -0,0 +1,49 @@
|
||||
.widget_wpcom_social_media_icons_widget ul {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.widget_wpcom_social_media_icons_widget ul li {
|
||||
border: 0 none;
|
||||
display: inline;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.widget_wpcom_social_media_icons_widget li a {
|
||||
border: 0 none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.widget_wpcom_social_media_icons_widget .genericon {
|
||||
font-family: 'Genericons';
|
||||
}
|
||||
|
||||
.widget_wpcom_social_media_icons_widget .screen-reader-text {
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
position: absolute !important;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.widget_wpcom_social_media_icons_widget .screen-reader-text:hover,
|
||||
.widget_wpcom_social_media_icons_widget .screen-reader-text:active,
|
||||
.widget_wpcom_social_media_icons_widget .screen-reader-text:focus {
|
||||
background-color: #f0f0f1;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
|
||||
clip: auto !important;
|
||||
color: #21759b;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
height: auto;
|
||||
left: 5px;
|
||||
line-height: normal;
|
||||
padding: 15px 23px 14px;
|
||||
text-decoration: none;
|
||||
top: 5px;
|
||||
width: auto;
|
||||
z-index: 100000; /* Above WP toolbar. */
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Top Posts widget.
|
||||
*
|
||||
* Currently, this widget depends on the Stats Module. To not load this file
|
||||
* when the Stats Module is not active would potentially bypass Jetpack's
|
||||
* fatal error detection on module activation, so we always load this file.
|
||||
* Instead, we don't register the widget if the Stats Module isn't active.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Redirect;
|
||||
use Automattic\Jetpack\Stats\WPCOM_Stats;
|
||||
use Automattic\Jetpack\Status;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
|
||||
// Register the widget for use in Appearance -> Widgets
|
||||
add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
|
||||
|
||||
/**
|
||||
* Register the widget, if the Stats module is active.
|
||||
*/
|
||||
function jetpack_top_posts_widget_init() {
|
||||
// Currently, this widget depends on the Stats Module.
|
||||
if (
|
||||
! ( defined( 'IS_WPCOM' ) && IS_WPCOM )
|
||||
&& (
|
||||
! Jetpack::is_module_active( 'stats' )
|
||||
|| ( new Status() )->is_offline_mode()
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
register_widget( 'Jetpack_Top_Posts_Widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget class.
|
||||
*/
|
||||
class Jetpack_Top_Posts_Widget extends WP_Widget {
|
||||
/**
|
||||
* Widget unique identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $alt_option_name = 'widget_stats_topposts';
|
||||
|
||||
/**
|
||||
* Widget default title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $default_title = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'top-posts',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Top Posts & Pages', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$this->default_title = __( 'Top Posts & Pages', 'jetpack' );
|
||||
|
||||
/**
|
||||
* Add explanation about how the statistics are calculated.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.3
|
||||
*/
|
||||
add_action( 'jetpack_widget_top_posts_after_fields', array( $this, 'stats_explanation' ) );
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Top Posts and Pages" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
// @TODO: Hide for Simple sites when the block API starts working.
|
||||
if ( ! ( new Host() )->is_wpcom_simple() ) {
|
||||
$widget_types[] = 'top-posts';
|
||||
}
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue stylesheet.
|
||||
*/
|
||||
public function enqueue_style() {
|
||||
wp_register_style( 'jetpack-top-posts-widget', plugins_url( 'top-posts/style.css', __FILE__ ), array(), '20141013' );
|
||||
wp_enqueue_style( 'jetpack-top-posts-widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the form for this widget on the Widgets page of the WP Admin area.
|
||||
*
|
||||
* @param array $instance Instance configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, static::defaults() );
|
||||
|
||||
if ( false === $instance['title'] ) {
|
||||
$instance['title'] = $this->default_title;
|
||||
}
|
||||
$title = stripslashes( $instance['title'] );
|
||||
|
||||
$count = isset( $instance['count'] ) ? (int) $instance['count'] : 10;
|
||||
if ( $count < 1 || 10 < $count ) {
|
||||
$count = 10;
|
||||
}
|
||||
|
||||
$allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
|
||||
$types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
|
||||
|
||||
// 'likes' are not available in Jetpack
|
||||
$ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering'] ? 'likes' : 'views';
|
||||
|
||||
if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
|
||||
$display = $instance['display'];
|
||||
} else {
|
||||
$display = 'text';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"><?php esc_html_e( 'Maximum number of posts to show (no more than 10):', 'jetpack' ); ?></label>
|
||||
<input id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>" type="number" value="<?php echo (int) $count; ?>" min="1" max="10" />
|
||||
</p>
|
||||
|
||||
<?php if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) : ?>
|
||||
<p>
|
||||
<label><?php esc_html_e( 'Order Top Posts & Pages By:', 'jetpack' ); ?></label>
|
||||
<ul>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'ordering' ) ); ?>-likes" name="<?php echo esc_attr( $this->get_field_name( 'ordering' ) ); ?>" type="radio" value="likes" <?php checked( 'likes', $ordering ); ?> /> <?php esc_html_e( 'Likes', 'jetpack' ); ?></label></li>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'ordering' ) ); ?>-views" name="<?php echo esc_attr( $this->get_field_name( 'ordering' ) ); ?>" type="radio" value="views" <?php checked( 'views', $ordering ); ?> /> <?php esc_html_e( 'Views', 'jetpack' ); ?></label></li>
|
||||
</ul>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'types' ) ); ?>"><?php esc_html_e( 'Types of pages to display:', 'jetpack' ); ?></label>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ( $allowed_post_types as $type ) {
|
||||
// Get the Post Type name to display next to the checkbox.
|
||||
$post_type_object = get_post_type_object( $type );
|
||||
$label = $post_type_object->labels->name;
|
||||
|
||||
$checked = '';
|
||||
if ( in_array( $type, $types, true ) ) {
|
||||
$checked = 'checked="checked" ';
|
||||
}
|
||||
?>
|
||||
|
||||
<li><label>
|
||||
<input
|
||||
value="<?php echo esc_attr( $type ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'types' ) ); ?>[]"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'types' ) . '-' . $type ); ?>"
|
||||
type="checkbox"
|
||||
<?php echo $checked; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
>
|
||||
<?php echo esc_html( $label ); ?>
|
||||
</label></li>
|
||||
|
||||
<?php } // End foreach ?>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label><?php esc_html_e( 'Display as:', 'jetpack' ); ?></label>
|
||||
<ul>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-text" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="text" <?php checked( 'text', $display ); ?> /> <?php esc_html_e( 'Text List', 'jetpack' ); ?></label></li>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-list" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="list" <?php checked( 'list', $display ); ?> /> <?php esc_html_e( 'Image List', 'jetpack' ); ?></label></li>
|
||||
<li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-grid" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="grid" <?php checked( 'grid', $display ); ?> /> <?php esc_html_e( 'Image Grid', 'jetpack' ); ?></label></li>
|
||||
</ul>
|
||||
</p>
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Fires after the fields are displayed in the Top Posts Widget settings in wp-admin.
|
||||
*
|
||||
* Allow adding extra content after the fields are displayed.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.3
|
||||
*
|
||||
* @param array $args {
|
||||
* @param array $instance The widget instance.
|
||||
* @param object $this The class object.
|
||||
* }
|
||||
*/
|
||||
do_action( 'jetpack_widget_top_posts_after_fields', array( $instance, $this ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Explains how the statics are calculated.
|
||||
*/
|
||||
public function stats_explanation() {
|
||||
echo '<p>';
|
||||
esc_html_e( 'Top Posts & Pages by views are calculated from 24-48 hours of stats. They take a while to change.', 'jetpack' );
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with the settings when they are saved by the admin.
|
||||
*
|
||||
* @param array $new_instance New configuration values.
|
||||
* @param array $old_instance Old configuration values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['title'] = wp_kses( $new_instance['title'], array() );
|
||||
if ( $instance['title'] === $this->default_title ) {
|
||||
$instance['title'] = false; // Store as false in case of language change.
|
||||
}
|
||||
|
||||
$instance['count'] = (int) $new_instance['count'];
|
||||
if ( $instance['count'] < 1 || 10 < $instance['count'] ) {
|
||||
$instance['count'] = 10;
|
||||
}
|
||||
|
||||
// 'likes' are not available in Jetpack
|
||||
$instance['ordering'] = isset( $new_instance['ordering'] ) && 'likes' === $new_instance['ordering']
|
||||
? 'likes'
|
||||
: 'views';
|
||||
|
||||
$allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
|
||||
$instance['types'] = $new_instance['types'];
|
||||
foreach ( $new_instance['types'] as $key => $type ) {
|
||||
if ( ! in_array( $type, $allowed_post_types, true ) ) {
|
||||
unset( $new_instance['types'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['display'] ) && in_array( $new_instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
|
||||
$instance['display'] = $new_instance['display'];
|
||||
} else {
|
||||
$instance['display'] = 'text';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters Top Posts Widget settings before they're saved.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.3
|
||||
*
|
||||
* @param array $instance The santized widget instance. Only contains data processed by the current widget.
|
||||
* @param array $new_instance The new widget instance before sanitization.
|
||||
*/
|
||||
$instance = apply_filters( 'jetpack_top_posts_saving', $instance, $new_instance );
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the HTML for this widget.
|
||||
*
|
||||
* @param array $args An array of standard parameters for widgets in this theme.
|
||||
* @param array $instance An array of settings for this widget instance.
|
||||
*
|
||||
* @return void Echoes it's output
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'top_posts' );
|
||||
|
||||
$instance = wp_parse_args( (array) $instance, static::defaults() );
|
||||
|
||||
$title = isset( $instance['title'] ) ? $instance['title'] : false;
|
||||
if ( false === $title ) {
|
||||
$title = $this->default_title;
|
||||
}
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $title );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_style();
|
||||
|
||||
$count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
|
||||
if ( $count < 1 || 10 < $count ) {
|
||||
$count = 10;
|
||||
}
|
||||
/**
|
||||
* Control the number of displayed posts.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param string $count Number of Posts displayed in the Top Posts widget. Default is 10.
|
||||
*/
|
||||
$count = apply_filters( 'jetpack_top_posts_widget_count', $count );
|
||||
|
||||
$types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
|
||||
|
||||
// 'likes' are not available in Jetpack
|
||||
$ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering']
|
||||
? 'likes'
|
||||
: 'views';
|
||||
|
||||
if (
|
||||
isset( $instance['display'] )
|
||||
&& in_array( $instance['display'], array( 'grid', 'list', 'text' ), true )
|
||||
) {
|
||||
$display = $instance['display'];
|
||||
} else {
|
||||
$display = 'text';
|
||||
}
|
||||
|
||||
$get_image_options = array();
|
||||
if ( 'text' !== $display ) {
|
||||
$get_image_options = array(
|
||||
'fallback_to_avatars' => true,
|
||||
/** This filter is documented in modules/stats.php */
|
||||
'gravatar_default' => apply_filters( 'jetpack_static_url', set_url_scheme( 'https://en.wordpress.com/i/logo/white-gray-80.png' ) ),
|
||||
'avatar_size' => 40,
|
||||
'width' => null,
|
||||
'height' => null,
|
||||
);
|
||||
if ( 'grid' === $display ) {
|
||||
$get_image_options['avatar_size'] = 200;
|
||||
}
|
||||
/**
|
||||
* Top Posts Widget Image options.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 1.8.0
|
||||
*
|
||||
* @param array $get_image_options {
|
||||
* Array of Image options.
|
||||
* @type bool true Should we default to Gravatars when no image is found? Default is true.
|
||||
* @type string $gravatar_default Default Image URL if no Gravatar is found.
|
||||
* @type int $avatar_size Default Image size.
|
||||
* @type mixed $width Image width, not set by default and $avatar_size is used instead.
|
||||
* @type mixed $height Image height, not set by default and $avatar_size is used instead.
|
||||
* }
|
||||
*/
|
||||
$get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
|
||||
}
|
||||
|
||||
if ( function_exists( 'wpl_get_blogs_most_liked_posts' ) && 'likes' === $ordering ) {
|
||||
$posts = $this->get_by_likes( $count, $types );
|
||||
} else {
|
||||
$posts = $this->get_by_views( $count, $args, $types );
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have no posts, add some fallback posts
|
||||
* and display a fallback message for admins.
|
||||
*/
|
||||
if ( ! $posts ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
echo self::fallback_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$posts = $this->get_fallback_posts( $count, $types );
|
||||
}
|
||||
|
||||
/*
|
||||
* Display our posts.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter the layout of the Top Posts Widget
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $layout layout of the Top Posts Widget (empty string).
|
||||
* @param array $posts IDs of the posts to be displayed.
|
||||
* @param array $display Display option from widget form.
|
||||
*/
|
||||
$layout = apply_filters( 'jetpack_top_posts_widget_layout', '', $posts, $display );
|
||||
if ( ! empty( $layout ) ) {
|
||||
echo $layout; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
switch ( $display ) {
|
||||
case 'list':
|
||||
case 'grid':
|
||||
// Keep the avatar_size as default dimensions for backward compatibility.
|
||||
$width = (int) $get_image_options['avatar_size'];
|
||||
$height = (int) $get_image_options['avatar_size'];
|
||||
|
||||
// Check if the user has changed the width.
|
||||
if ( ! empty( $get_image_options['width'] ) ) {
|
||||
$width = (int) $get_image_options['width'];
|
||||
}
|
||||
|
||||
// Check if the user has changed the height.
|
||||
if ( ! empty( $get_image_options['height'] ) ) {
|
||||
$height = (int) $get_image_options['height'];
|
||||
}
|
||||
|
||||
foreach ( $posts as &$post ) {
|
||||
$image = Jetpack_PostImages::get_image(
|
||||
$post['post_id'],
|
||||
array(
|
||||
'fallback_to_avatars' => (bool) $get_image_options['fallback_to_avatars'],
|
||||
'width' => (int) $width,
|
||||
'height' => (int) $height,
|
||||
'avatar_size' => (int) $get_image_options['avatar_size'],
|
||||
)
|
||||
);
|
||||
|
||||
if ( $image ) {
|
||||
$post['image'] = Jetpack_PostImages::fit_image_url(
|
||||
$image['src'],
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
|
||||
$post['image_srcset'] = Jetpack_PostImages::generate_cropped_srcset(
|
||||
$image,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
|
||||
if ( empty( $post['image_srcset'] ) ) {
|
||||
$post['image_srcset'] = "{$post['image']} 1x";
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $post );
|
||||
|
||||
if ( 'grid' === $display ) {
|
||||
echo "<div class='widgets-grid-layout no-grav'>\n";
|
||||
foreach ( $posts as $post ) {
|
||||
echo '<div class="widget-grid-view-image">';
|
||||
|
||||
/**
|
||||
* Fires before each Top Post result, inside <li>.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param string $post['post_id'] Post ID.
|
||||
*/
|
||||
do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
|
||||
|
||||
/**
|
||||
* Filter the permalink of items in the Top Posts widget.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $post['permalink'] Post permalink.
|
||||
* @param array $post Post array.
|
||||
*/
|
||||
$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
|
||||
|
||||
if ( $post['image'] ) {
|
||||
printf(
|
||||
'<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img loading="lazy" width="%4$d" height="%5$d" src="%6$s" srcset="%7$s" alt="%2$s" data-pin-nopin="true"/></a>',
|
||||
esc_url( $filtered_permalink ),
|
||||
esc_attr( wp_kses( $post['title'], array() ) ),
|
||||
( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
|
||||
absint( $width ),
|
||||
absint( $height ),
|
||||
esc_url( $post['image'] ),
|
||||
esc_attr( $post['image_srcset'] )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after each Top Post result, inside <li>.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param string $post['post_id'] Post ID.
|
||||
*/
|
||||
do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
echo "</div>\n";
|
||||
} else {
|
||||
echo "<ul class='widgets-list-layout no-grav'>\n";
|
||||
foreach ( $posts as $post ) {
|
||||
echo '<li>';
|
||||
|
||||
/** This action is documented in modules/widgets/top-posts.php */
|
||||
do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
|
||||
|
||||
/** This filter is documented in modules/widgets/top-posts.php */
|
||||
$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
|
||||
|
||||
if ( $post['image'] ) {
|
||||
printf(
|
||||
'<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img loading="lazy" width="%4$d" height="%5$d" src="%6$s" srcset="%7$s" alt="%2$s" data-pin-nopin="true" class="widgets-list-layout-blavatar" /></a>',
|
||||
esc_url( $filtered_permalink ),
|
||||
esc_attr( wp_kses( $post['title'], array() ) ),
|
||||
( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
|
||||
absint( $width ),
|
||||
absint( $height ),
|
||||
esc_url( $post['image'] ),
|
||||
esc_attr( $post['image_srcset'] )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="widgets-list-layout-links">
|
||||
<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%4$s</a>
|
||||
</div>
|
||||
',
|
||||
esc_url( $filtered_permalink ),
|
||||
esc_attr( wp_kses( $post['title'], array() ) ),
|
||||
( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
|
||||
esc_html( wp_kses( $post['title'], array() ) )
|
||||
);
|
||||
|
||||
/** This action is documented in modules/widgets/top-posts.php */
|
||||
do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
echo "</ul>\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '<ul>';
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
echo '<li>';
|
||||
|
||||
/** This action is documented in modules/widgets/top-posts.php */
|
||||
do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
|
||||
|
||||
/** This filter is documented in modules/widgets/top-posts.php */
|
||||
$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
|
||||
|
||||
printf(
|
||||
'<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
|
||||
esc_url( $filtered_permalink ),
|
||||
( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
|
||||
esc_html( wp_kses( $post['title'], array() ) )
|
||||
);
|
||||
|
||||
/** This action is documented in modules/widgets/top-posts.php */
|
||||
do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
break;
|
||||
}
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a message with recommendations when there are no recorded top posts.
|
||||
*
|
||||
* @return string $fallback_message
|
||||
*/
|
||||
private static function fallback_message() {
|
||||
$link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
|
||||
}
|
||||
|
||||
$fallback_message = '<p>';
|
||||
$fallback_message .= sprintf(
|
||||
wp_kses(
|
||||
/* Translators: Placeholder: link to the Jetpack support article. */
|
||||
__( 'There are no popular posts to display. Instead, your visitors will see a list of your recent posts below. <a href="%s" target="_blank">Want more traffic?</a>', 'jetpack' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( $link )
|
||||
);
|
||||
$fallback_message .= '<p>';
|
||||
|
||||
return $fallback_message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget default option values.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return array(
|
||||
'title' => esc_html__( 'Top Posts & Pages', 'jetpack' ),
|
||||
'count' => absint( 10 ),
|
||||
'types' => array( 'post', 'page' ),
|
||||
'ordering' => 'views',
|
||||
'display' => 'text',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get most liked posts
|
||||
*
|
||||
* ONLY TO BE USED IN WPCOM
|
||||
*
|
||||
* @since 8.4.0 Added $types param
|
||||
*
|
||||
* @param int $count The maximum number of posts to be returned.
|
||||
* @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
|
||||
*
|
||||
* @return array array of posts.
|
||||
*/
|
||||
public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
|
||||
$post_likes = wpl_get_blogs_most_liked_posts();
|
||||
if ( ! $post_likes ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->get_posts( array_keys( $post_likes ), $count, $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top posts based on views
|
||||
*
|
||||
* @since 8.4.0 Added $types param
|
||||
*
|
||||
* @param int $count The maximum number of posts to be returned.
|
||||
* @param array $args The widget arguments.
|
||||
* @param array $types The post types that should be returned.
|
||||
*
|
||||
* @return array array of posts. Defaults to 'post' and 'page'.
|
||||
*/
|
||||
public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
|
||||
if ( false === $post_views ) {
|
||||
$stats_get_daily_history = stats_get_daily_history(
|
||||
false,
|
||||
get_current_blog_id(),
|
||||
'postviews',
|
||||
'post_id',
|
||||
false,
|
||||
2,
|
||||
'',
|
||||
$count * 2 + 10,
|
||||
true
|
||||
);
|
||||
$post_views = array_shift( $stats_get_daily_history );
|
||||
unset( $post_views[0] );
|
||||
wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
|
||||
}
|
||||
|
||||
return $this->get_posts( array_keys( $post_views ), $count, $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the number of days used to calculate Top Posts for the Top Posts widget.
|
||||
* We do not recommend accessing more than 10 days of results at one.
|
||||
* When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
|
||||
* Querying for -1 days will give results for an infinite number of days.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.9.3
|
||||
*
|
||||
* @param int 2 Number of days. Default is 2.
|
||||
* @param array $args The widget arguments.
|
||||
*/
|
||||
$days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
|
||||
|
||||
/** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
|
||||
if ( 0 === $days || false === $days ) {
|
||||
$days = 2;
|
||||
}
|
||||
|
||||
$query_args = array(
|
||||
'max' => 11,
|
||||
'summarize' => 1,
|
||||
'num' => (int) $days,
|
||||
);
|
||||
$wpcom_stats = new WPCOM_Stats();
|
||||
$post_view_posts = $wpcom_stats->convert_stats_array_to_object(
|
||||
$wpcom_stats->get_top_posts( $query_args )
|
||||
);
|
||||
|
||||
if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
|
||||
|
||||
if ( ! $post_view_ids ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->get_posts( $post_view_ids, $count, $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get some posts if no posts are found in the stats API
|
||||
*
|
||||
* @since 8.4.0 Added $count and $types
|
||||
*
|
||||
* @param int $count The maximum number of posts to be returned.
|
||||
* @param array $types The post types that should be returned.
|
||||
* @return array
|
||||
*/
|
||||
public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
|
||||
$post_query = new WP_Query();
|
||||
|
||||
if ( ! is_array( $types ) || empty( $types ) ) {
|
||||
$types = array( 'post', 'page' );
|
||||
}
|
||||
|
||||
$posts = $post_query->query(
|
||||
array(
|
||||
'posts_per_page' => $count,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => $types,
|
||||
'no_found_rows' => true,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $posts ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->get_posts( $posts, $count, $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts from an array of IDs
|
||||
*
|
||||
* @since 8.4.0 Added $types parameters
|
||||
*
|
||||
* @param array $post_ids The post IDs.
|
||||
* @param int $count The maximum number of posts to return.
|
||||
* @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
|
||||
* @return array
|
||||
*/
|
||||
public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
|
||||
$counter = 0;
|
||||
|
||||
if ( ! is_array( $types ) || empty( $types ) ) {
|
||||
$types = array( 'post', 'page' );
|
||||
}
|
||||
|
||||
$posts = array();
|
||||
foreach ( (array) $post_ids as $post_id ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachment pages use the 'inherit' post status by default.
|
||||
* To be able to remove attachment pages from private and password protect posts,
|
||||
* we need to replace their post status by the parent post' status.
|
||||
*/
|
||||
if ( 'inherit' === $post->post_status && 'attachment' === $post->post_type ) {
|
||||
$post->post_status = get_post_status( $post_id );
|
||||
}
|
||||
|
||||
// hide private and password protected posts.
|
||||
if ( 'publish' !== $post->post_status || ! empty( $post->post_password ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter by chosen Post Types.
|
||||
if ( ! in_array( $post->post_type, $types, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both get HTML stripped etc on display.
|
||||
if ( empty( $post->post_title ) ) {
|
||||
$title_source = $post->post_content;
|
||||
$title = wp_html_excerpt( $title_source, 50 );
|
||||
$title .= '…';
|
||||
} else {
|
||||
$title = $post->post_title;
|
||||
}
|
||||
|
||||
$permalink = get_permalink( $post->ID );
|
||||
|
||||
$post_type = $post->post_type;
|
||||
|
||||
$posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
|
||||
++$counter;
|
||||
|
||||
if ( $counter == $count ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
|
||||
break; // only need to load and show x number of likes.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the Top Posts and Pages.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param array $posts Array of the most popular posts.
|
||||
* @param array $post_ids Array of Post IDs.
|
||||
* @param string $count Number of Top Posts we want to display.
|
||||
*/
|
||||
return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shortcode to display the widget anywhere.
|
||||
*
|
||||
* @since 3.9.2
|
||||
*
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
function jetpack_do_top_posts_widget( $instance ) {
|
||||
// Post Types can't be entered as an array in the shortcode parameters.
|
||||
if ( isset( $instance['types'] ) && is_string( $instance['types'] ) ) {
|
||||
$instance['types'] = explode( ',', $instance['types'] );
|
||||
}
|
||||
|
||||
$instance = shortcode_atts(
|
||||
Jetpack_Top_Posts_Widget::defaults(),
|
||||
$instance,
|
||||
'jetpack_top_posts_widget'
|
||||
);
|
||||
|
||||
// Add a class to allow styling.
|
||||
$args = array(
|
||||
'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
|
||||
);
|
||||
|
||||
ob_start();
|
||||
the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
|
||||
$output = ob_get_clean();
|
||||
|
||||
return $output;
|
||||
}
|
||||
add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Top Posts Widget styles for Jetpack
|
||||
*/
|
||||
|
||||
/* 2-Column Grid Layout */
|
||||
|
||||
.widgets-grid-layout {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.widgets-grid-layout:before,
|
||||
.widgets-grid-layout:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.widgets-grid-layout:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.widget-grid-view-image {
|
||||
float: left;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.widget-grid-view-image a {
|
||||
display: block;
|
||||
margin: 0 2px 4px 0;
|
||||
}
|
||||
|
||||
.widget-grid-view-image:nth-child(even) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.widget-grid-view-image:nth-child(even) a {
|
||||
margin: 0 0 4px 2px;
|
||||
}
|
||||
|
||||
.widgets-grid-layout .widget-grid-view-image img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Multi-Column Grid Layout */
|
||||
|
||||
.widgets-multi-column-grid ul {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid ul li {
|
||||
background: none;
|
||||
clear: none;
|
||||
float: left;
|
||||
margin: 0 -5px -3px 0;
|
||||
padding: 0 8px 6px 0;
|
||||
border: none;
|
||||
list-style-type: none !important;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid ul li a {
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.widgets-multi-column-grid .avatar {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* List Layout */
|
||||
|
||||
.widgets-list-layout {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.widgets-list-layout li:before,
|
||||
.widgets-list-layout li:after {
|
||||
content:"";
|
||||
display:table;
|
||||
}
|
||||
.widgets-list-layout li:after {
|
||||
clear:both;
|
||||
}
|
||||
.widgets-list-layout li {
|
||||
zoom:1;
|
||||
margin-bottom: 1em;
|
||||
list-style-type: none !important;
|
||||
}
|
||||
|
||||
.widgets-list-layout .widgets-list-layout-blavatar {
|
||||
float: left;
|
||||
width: 21.276596%;
|
||||
max-width: 40px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.widgets-list-layout-links {
|
||||
float: right;
|
||||
width: 73.404255%;
|
||||
}
|
||||
|
||||
.widgets-list-layout span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.widgets-list-layout span:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
jQuery( function ( $ ) {
|
||||
function twitterWidgetTypeChanged( widgetTypeSelector ) {
|
||||
var selectedType = $( widgetTypeSelector ).val();
|
||||
$( widgetTypeSelector )
|
||||
.closest( '.jetpack-twitter-timeline-widget-type-container' )
|
||||
.next( '.jetpack-twitter-timeline-widget-id-container' )
|
||||
.find( 'label' )
|
||||
.css( 'display', function () {
|
||||
var labelType = $( this ).data( 'widget-type' );
|
||||
if ( selectedType === labelType ) {
|
||||
return '';
|
||||
}
|
||||
return 'none';
|
||||
} );
|
||||
}
|
||||
|
||||
function twitterWidgetTweetDisplayChanged( event ) {
|
||||
var $tweetDisplaySelector = $( event.target );
|
||||
var selectedTweetDisplay = $tweetDisplaySelector.val();
|
||||
var $form = $tweetDisplaySelector.closest( 'form' );
|
||||
var $heightContainer = $form.find( '.jetpack-twitter-timeline-widget-height-container' );
|
||||
var $tweetLimitContainer = $form.find(
|
||||
'.jetpack-twitter-timeline-widget-tweet-limit-container'
|
||||
);
|
||||
var $scrollbarInput = $form.find( 'input[id*=chrome-noscrollbar]' );
|
||||
switch ( selectedTweetDisplay ) {
|
||||
case 'fixed':
|
||||
$heightContainer.hide();
|
||||
$tweetLimitContainer.show();
|
||||
$scrollbarInput.prop( 'disabled', true );
|
||||
break;
|
||||
case 'dynamic':
|
||||
$tweetLimitContainer.hide();
|
||||
$heightContainer.show();
|
||||
$scrollbarInput.prop( 'disabled', false );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We could either be in wp-admin/widgets.php or the Customizer.
|
||||
var $container = $( '#customize-controls' );
|
||||
if ( ! $container.length ) {
|
||||
$container = $( '#wpbody' );
|
||||
}
|
||||
|
||||
// Observe widget settings for 'change' events of the 'type' property for
|
||||
// current and future Twitter timeline widgets.
|
||||
$container.on( 'change', '.jetpack-twitter-timeline-widget-type', function () {
|
||||
twitterWidgetTypeChanged( this );
|
||||
} );
|
||||
|
||||
// Set the labels for currently existing widgets (including the "template"
|
||||
// version that is copied when a new widget is added).
|
||||
$container.find( '.jetpack-twitter-timeline-widget-type' ).each( function () {
|
||||
twitterWidgetTypeChanged( this );
|
||||
} );
|
||||
|
||||
// Observe widget settings for 'change' events of the 'tweet-display' property for
|
||||
// current and future Twitter timeline widgets.
|
||||
$container.on(
|
||||
'change',
|
||||
'.jetpack-twitter-timeline-widget-tweet-display-radio',
|
||||
twitterWidgetTweetDisplayChanged
|
||||
);
|
||||
} );
|
||||
@@ -0,0 +1,595 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Twitter Timeline Widget.
|
||||
*
|
||||
* Based on Evolution Twitter Timeline
|
||||
* (https://wordpress.org/extend/plugins/evolution-twitter-timeline/)
|
||||
* For details on Twitter Timelines see:
|
||||
* - https://twitter.com/settings/widgets
|
||||
* - https://dev.twitter.com/docs/embedded-timelines
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Redirect;
|
||||
|
||||
/**
|
||||
* Register the widget for use in Appearance -> Widgets
|
||||
*/
|
||||
function jetpack_twitter_timeline_widget_init() {
|
||||
register_widget( 'Jetpack_Twitter_Timeline_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'jetpack_twitter_timeline_widget_init' );
|
||||
|
||||
/**
|
||||
* Widget class.
|
||||
*/
|
||||
class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
|
||||
/**
|
||||
* Register widget with WordPress.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'twitter_timeline',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', esc_html__( 'Twitter Timeline', 'jetpack' ) ),
|
||||
array(
|
||||
'classname' => 'widget_twitter_timeline',
|
||||
'description' => __( 'Display an official Twitter Embedded Timeline widget.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
|
||||
add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the "Twitter Timeline" widget from the Legacy Widget block
|
||||
*
|
||||
* @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
|
||||
* @return array $widget_types New list of widgets that will be removed.
|
||||
*/
|
||||
public function hide_widget_in_block_editor( $widget_types ) {
|
||||
$widget_types[] = 'twitter_timeline';
|
||||
return $widget_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) {
|
||||
wp_enqueue_script( 'jetpack-twitter-timeline' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue script to improve admin UI
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public function admin_scripts( $hook ) {
|
||||
// This is still 'widgets.php' when managing widgets via the Customizer.
|
||||
if ( 'widgets.php' === $hook ) {
|
||||
wp_enqueue_script(
|
||||
'twitter-timeline-admin',
|
||||
Assets::get_file_url_for_environment(
|
||||
'_inc/build/widgets/twitter-timeline-admin.min.js',
|
||||
'modules/widgets/twitter-timeline-admin.js'
|
||||
),
|
||||
array( 'jquery' ),
|
||||
JETPACK__VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end display of widget.
|
||||
*
|
||||
* @see WP_Widget::widget()
|
||||
*
|
||||
* @param array $args Widget arguments.
|
||||
* @param array $instance Saved values from database.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$output = '';
|
||||
|
||||
// Twitter deprecated `data-widget-id` on 2018-05-25,
|
||||
// with cease support deadline on 2018-07-27.
|
||||
if ( isset( $instance['type'] ) && 'widget-id' === $instance['type'] ) {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
$output .= $args['before_widget']
|
||||
. $args['before_title'] . esc_html__( 'Twitter Timeline', 'jetpack' ) . $args['after_title']
|
||||
. '<p>' . esc_html__( "The Twitter Timeline widget can't display tweets based on searches or hashtags. To display a simple list of tweets instead, change the Widget ID to a Twitter username. Otherwise, delete this widget.", 'jetpack' ) . '</p>'
|
||||
. '<p>' . esc_html__( '(Only administrators will see this message.)', 'jetpack' ) . '</p>'
|
||||
. $args['after_widget'];
|
||||
}
|
||||
|
||||
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$instance['lang'] = substr( strtoupper( get_locale() ), 0, 2 );
|
||||
|
||||
$output .= $args['before_widget'];
|
||||
|
||||
$title = isset( $instance['title'] ) ? $instance['title'] : '';
|
||||
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$title = apply_filters( 'widget_title', $title );
|
||||
if ( ! empty( $title ) ) {
|
||||
$output .= $args['before_title'] . $title . $args['after_title'];
|
||||
}
|
||||
|
||||
$possible_data_attribs = array(
|
||||
'width',
|
||||
'height',
|
||||
'theme',
|
||||
'border-color',
|
||||
'tweet-limit',
|
||||
'lang',
|
||||
);
|
||||
$data_attrs = '';
|
||||
foreach ( $possible_data_attribs as $att ) {
|
||||
if ( ! empty( $instance[ $att ] ) && ! is_array( $instance[ $att ] ) ) {
|
||||
$data_attrs .= ' data-' . esc_attr( $att ) . '="' . esc_attr( $instance[ $att ] ) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
/** This filter is documented in modules/shortcodes/tweet.php */
|
||||
$partner = apply_filters( 'jetpack_twitter_partner_id', 'jetpack' );
|
||||
if ( ! empty( $partner ) ) {
|
||||
$data_attrs .= ' data-partner="' . esc_attr( $partner ) . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the activation of Do Not Track for the Twitter Timeline Widget.
|
||||
*
|
||||
* @see https://developer.twitter.com/en/docs/twitter-for-websites/timelines/guides/parameter-reference.html
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param bool false Should the Twitter Timeline use the DNT attribute? Default to false.
|
||||
*/
|
||||
$dnt = apply_filters( 'jetpack_twitter_timeline_default_dnt', false );
|
||||
if ( true === $dnt ) {
|
||||
$data_attrs .= ' data-dnt="true"';
|
||||
}
|
||||
|
||||
if ( ! empty( $instance['chrome'] ) && is_array( $instance['chrome'] ) ) {
|
||||
$data_attrs .= ' data-chrome="' . esc_attr( implode( ' ', $instance['chrome'] ) ) . '"';
|
||||
}
|
||||
|
||||
$timeline_placeholder = __( 'My Tweets', 'jetpack' );
|
||||
|
||||
/**
|
||||
* Filter the Timeline placeholder text.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $timeline_placeholder Timeline placeholder text.
|
||||
*/
|
||||
$timeline_placeholder = apply_filters( 'jetpack_twitter_timeline_placeholder', $timeline_placeholder );
|
||||
|
||||
$type = ( isset( $instance['type'] ) ? $instance['type'] : '' );
|
||||
$widget_id = ( isset( $instance['widget-id'] ) ? $instance['widget-id'] : '' );
|
||||
|
||||
if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
|
||||
$width = ! empty( $instance['width'] ) ? $instance['width'] : 600;
|
||||
$height = ! empty( $instance['height'] ) ? $instance['height'] : 480;
|
||||
$output .= '<amp-twitter' . $data_attrs . ' layout="responsive" data-timeline-source-type="profile" data-timeline-screen-name="' . esc_attr( $widget_id ) . '" width="' . absint( $width ) . '" height="' . absint( $height ) . '">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$output .= esc_html( $timeline_placeholder ) . '</amp-twitter>';
|
||||
|
||||
echo $output . $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
// Start tag output
|
||||
// This tag is transformed into the widget markup by Twitter's
|
||||
// widgets.js code.
|
||||
$output .= '<a class="twitter-timeline"' . $data_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
switch ( $type ) {
|
||||
case 'profile':
|
||||
$output .= ' href="https://twitter.com/' . esc_attr( $widget_id ) . '"';
|
||||
break;
|
||||
case 'widget-id':
|
||||
default:
|
||||
$output .= ' data-widget-id="' . esc_attr( $widget_id ) . '"';
|
||||
break;
|
||||
}
|
||||
$output .= ' href="https://twitter.com/' . esc_attr( $widget_id ) . '"';
|
||||
|
||||
// End tag output.
|
||||
$output .= '>';
|
||||
|
||||
$output .= esc_html( $timeline_placeholder ) . '</a>';
|
||||
|
||||
// End tag output.
|
||||
|
||||
echo $output . $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'twitter_timeline' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget form values as they are saved.
|
||||
*
|
||||
* @see WP_Widget::update()
|
||||
*
|
||||
* @param array $new_instance Values just sent to be saved.
|
||||
* @param array $old_instance Previously saved values from database.
|
||||
*
|
||||
* @return array Updated safe values to be saved.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
|
||||
$instance['title'] = sanitize_text_field( $new_instance['title'] );
|
||||
|
||||
$width = (int) $new_instance['width'];
|
||||
if ( $width ) {
|
||||
// From publish.twitter.com: 220 <= width <= 1200.
|
||||
$instance['width'] = min( max( $width, 220 ), 1200 );
|
||||
} else {
|
||||
// Set default width value to minimum.
|
||||
$instance['width'] = 220;
|
||||
}
|
||||
|
||||
$tweet_display = sanitize_text_field( $new_instance['tweet-display'] );
|
||||
$instance['tweet-display'] = $tweet_display;
|
||||
/**
|
||||
* A timeline with a specified limit is expanded to the height of those Tweets.
|
||||
* The specified height value no longer applies, so reject the height value
|
||||
* when a valid limit is set: a widget attempting to save both limit 5 and
|
||||
* height 400 would be saved with just limit 5.
|
||||
* So if the tweet display option is set to 'dynamic' the limit will be unset and we'll
|
||||
* take into account the height value.
|
||||
* If the tweet display option is set to 'fixed' the height will be unset and we'll
|
||||
* take into account the limit value.
|
||||
*/
|
||||
$instance['height'] = '';
|
||||
$instance['tweet-limit'] = null;
|
||||
|
||||
switch ( $tweet_display ) {
|
||||
case 'dynamic':
|
||||
$height = (int) $new_instance['height'];
|
||||
// From publish.twitter.com: height >= 200.
|
||||
$instance['height'] = max( $height, 200 );
|
||||
break;
|
||||
case 'fixed':
|
||||
$tweet_limit = (int) $new_instance['tweet-limit'];
|
||||
// From publish.twitter.com: 1 >= tweet-limit >= 20.
|
||||
$instance['tweet-limit'] = min( max( $tweet_limit, 1 ), 20 );
|
||||
break;
|
||||
}
|
||||
|
||||
// If they entered something that might be a full URL, try to parse it out.
|
||||
if ( is_string( $new_instance['widget-id'] ) ) {
|
||||
if ( preg_match(
|
||||
'#https?://twitter\.com/settings/widgets/(\d+)#s',
|
||||
$new_instance['widget-id'],
|
||||
$matches
|
||||
) ) {
|
||||
$new_instance['widget-id'] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
$instance['widget-id'] = sanitize_text_field( $new_instance['widget-id'] );
|
||||
|
||||
$new_border_color = sanitize_hex_color( $new_instance['border-color'] );
|
||||
if ( ! empty( $new_border_color ) ) {
|
||||
$instance['border-color'] = $new_border_color;
|
||||
}
|
||||
|
||||
$instance['type'] = 'profile';
|
||||
|
||||
$instance['theme'] = 'light';
|
||||
if ( in_array( $new_instance['theme'], array( 'light', 'dark' ), true ) ) {
|
||||
$instance['theme'] = $new_instance['theme'];
|
||||
}
|
||||
|
||||
$instance['chrome'] = array();
|
||||
$chrome_settings = array(
|
||||
'noheader',
|
||||
'nofooter',
|
||||
'noborders',
|
||||
'transparent',
|
||||
'noscrollbar',
|
||||
);
|
||||
|
||||
foreach ( $chrome_settings as $chrome ) {
|
||||
switch ( $chrome ) {
|
||||
case 'noheader':
|
||||
case 'nofooter':
|
||||
case 'noborders':
|
||||
case 'noscrollbar':
|
||||
if ( ! isset( $new_instance['chrome'] ) || ! in_array( $chrome, $new_instance['chrome'], true ) ) {
|
||||
$instance['chrome'][] = $chrome;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( isset( $new_instance['chrome'] ) && in_array( $chrome, $new_instance['chrome'], true ) ) {
|
||||
$instance['chrome'][] = $chrome;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a link to the documentation for a feature of this widget on
|
||||
* Jetpack or WordPress.com.
|
||||
*
|
||||
* @param string $hash anchor to potentially append to URL to target specific paragraph in page.
|
||||
*/
|
||||
public function get_docs_link( $hash = '' ) {
|
||||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
$base_url = 'https://wordpress.com/support/widgets/twitter-timeline-widget/';
|
||||
} else {
|
||||
$base_url = esc_url( Redirect::get_url( 'jetpack-support-extra-sidebar-widgets-twitter-timeline-widget' ) );
|
||||
}
|
||||
return '<a class="widget-access-link" href="' . $base_url . $hash . '" target="_blank"> Need help?</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Back end widget form.
|
||||
*
|
||||
* @see WP_Widget::form()
|
||||
*
|
||||
* @param array $instance Previously saved values from database.
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$defaults = array(
|
||||
'title' => esc_html__( 'Follow me on Twitter', 'jetpack' ),
|
||||
'width' => '220',
|
||||
'height' => '200',
|
||||
'type' => 'profile',
|
||||
'widget-id' => '',
|
||||
'border-color' => '#f0f0f1',
|
||||
'theme' => 'light',
|
||||
'chrome' => array(),
|
||||
'tweet-limit' => 1,
|
||||
'tweet-display' => 'dynamic',
|
||||
);
|
||||
|
||||
$instance = wp_parse_args( (array) $instance, $defaults );
|
||||
|
||||
if ( 'widget-id' === $instance['type'] ) {
|
||||
$instance['widget-id'] = '';
|
||||
}
|
||||
|
||||
$instance['type'] = 'profile';
|
||||
|
||||
/**
|
||||
* Set the tweet-display option to 'fixed' if height is empty and tweet-limit set
|
||||
* to ensure backwards compatibility with pre-existing widgets.
|
||||
*/
|
||||
if ( empty( $instance['height'] ) && isset( $instance['tweet-limit'] ) ) {
|
||||
$instance['tweet-display'] = 'fixed';
|
||||
}
|
||||
?>
|
||||
|
||||
<p class="jetpack-twitter-timeline-widget-id-container">
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'widget-id' ) ); ?>">
|
||||
<?php esc_html_e( 'Twitter username:', 'jetpack' ); ?>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
$this->get_docs_link( '#twitter-username' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'rel' => array(),
|
||||
'target' => array(),
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'widget-id' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'widget-id' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['widget-id'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
|
||||
<?php esc_html_e( 'Title:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['title'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<strong><?php esc_html_e( 'Number of tweets shown:', 'jetpack' ); ?></strong>
|
||||
</label>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'tweet-display' ) ); ?>-dynamic"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'tweet-display' ) ); ?>"
|
||||
type="radio"
|
||||
class="jetpack-twitter-timeline-widget-tweet-display-radio"
|
||||
value="dynamic"
|
||||
<?php checked( 'dynamic', $instance['tweet-display'] ); ?>
|
||||
/>
|
||||
<?php esc_html_e( 'Dynamic', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'tweet-display' ) ); ?>-fixed"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'tweet-display' ) ); ?>"
|
||||
type="radio"
|
||||
class="jetpack-twitter-timeline-widget-tweet-display-radio"
|
||||
value="fixed"
|
||||
<?php checked( 'fixed', $instance['tweet-display'] ); ?>
|
||||
/>
|
||||
<?php esc_html_e( 'Fixed', 'jetpack' ); ?>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p class="jetpack-twitter-timeline-widget-height-container" <?php echo ( 'fixed' === $instance['tweet-display'] ) ? ' style="display:none;"' : ''; ?>>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>">
|
||||
<?php esc_html_e( 'Height (in pixels; at least 200):', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'height' ) ); ?>"
|
||||
type="number" min="200"
|
||||
value="<?php echo esc_attr( $instance['height'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p class="jetpack-twitter-timeline-widget-tweet-limit-container" <?php echo ( 'dynamic' === $instance['tweet-display'] ) ? ' style="display:none;"' : ''; ?>>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'tweet-limit' ) ); ?>">
|
||||
<?php esc_html_e( 'Number of tweets in the timeline (1 to 20):', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'tweet-limit' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'tweet-limit' ) ); ?>"
|
||||
type="number" min="1" max="20"
|
||||
value="<?php echo esc_attr( $instance['tweet-limit'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>">
|
||||
<?php esc_html_e( 'Maximum width (in pixels; 220 to 1200):', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'width' ) ); ?>"
|
||||
type="number" min="220" max="1200"
|
||||
value="<?php echo esc_attr( $instance['width'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-noheader' ) ); ?>">
|
||||
<strong><?php esc_html_e( 'Layout options:', 'jetpack' ); ?></strong>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
type="checkbox"
|
||||
<?php checked( false, in_array( 'noheader', $instance['chrome'], true ) ); ?>
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'chrome-noheader' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]"
|
||||
value="noheader"
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-noheader' ) ); ?>">
|
||||
<?php esc_html_e( 'Show header', 'jetpack' ); ?>
|
||||
</label>
|
||||
<br />
|
||||
<input
|
||||
type="checkbox"
|
||||
<?php checked( false, in_array( 'nofooter', $instance['chrome'], true ) ); ?>
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'chrome-nofooter' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]"
|
||||
value="nofooter"
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-nofooter' ) ); ?>">
|
||||
<?php esc_html_e( 'Show footer', 'jetpack' ); ?>
|
||||
</label>
|
||||
<br />
|
||||
<input
|
||||
type="checkbox"
|
||||
<?php checked( false, in_array( 'noborders', $instance['chrome'], true ) ); ?>
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'chrome-noborders' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]"
|
||||
value="noborders"
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-noborders' ) ); ?>">
|
||||
<?php esc_html_e( 'Show borders', 'jetpack' ); ?>
|
||||
</label>
|
||||
<br />
|
||||
<input
|
||||
type="checkbox"
|
||||
<?php checked( false, in_array( 'noscrollbar', $instance['chrome'], true ) ); ?>
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'chrome-noscrollbar' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]"
|
||||
value="noscrollbar"
|
||||
<?php disabled( 'fixed', $instance['tweet-display'] ); ?>
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-noscrollbar' ) ); ?>">
|
||||
<?php esc_html_e( 'Show scrollbar', 'jetpack' ); ?>
|
||||
</label>
|
||||
<br />
|
||||
<input
|
||||
type="checkbox"
|
||||
<?php checked( in_array( 'transparent', $instance['chrome'], true ) ); ?>
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'chrome-transparent' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'chrome' ) ); ?>[]"
|
||||
value="transparent"
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'chrome-transparent' ) ); ?>">
|
||||
<?php esc_html_e( 'Transparent background', 'jetpack' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'border-color' ) ); ?>">
|
||||
<?php esc_html_e( 'Border color (in hex format):', 'jetpack' ); ?>
|
||||
</label>
|
||||
<input
|
||||
class="widefat"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'border-color' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'border-color' ) ); ?>"
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $instance['border-color'] ); ?>"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>">
|
||||
<?php esc_html_e( 'Color scheme:', 'jetpack' ); ?>
|
||||
</label>
|
||||
<select
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'theme' ) ); ?>"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>"
|
||||
class="widefat"
|
||||
>
|
||||
<option value="light"<?php selected( $instance['theme'], 'light' ); ?>>
|
||||
<?php esc_html_e( 'Light', 'jetpack' ); ?>
|
||||
</option>
|
||||
<option value="dark"<?php selected( $instance['theme'], 'dark' ); ?>>
|
||||
<?php esc_html_e( 'Dark', 'jetpack' ); ?>
|
||||
</option>
|
||||
</select>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Upcoming Events widget
|
||||
*
|
||||
* It relies on the icalendar-reader library.
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
|
||||
|
||||
/**
|
||||
* Register the widget.
|
||||
*/
|
||||
function upcoming_events_register_widgets() {
|
||||
register_widget( Jetpack_Upcoming_Events_Widget::class );
|
||||
}
|
||||
add_action( 'widgets_init', 'upcoming_events_register_widgets' );
|
||||
|
||||
/**
|
||||
* Widget class.
|
||||
*/
|
||||
class Jetpack_Upcoming_Events_Widget extends WP_Widget {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'upcoming_events_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Upcoming Events', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'Display upcoming events from an iCalendar feed.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
if ( is_active_widget( false, false, $this->id_base ) ) {
|
||||
add_action( 'wp_head', array( $this, 'css' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output CSS in the header everywhere where the widget is active.
|
||||
*/
|
||||
public function css() {
|
||||
?>
|
||||
<style type="text/css">
|
||||
.upcoming-events li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.upcoming-events li span {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the form for this widget on the Widgets page of the WP Admin area.
|
||||
*
|
||||
* @param array $instance Instance configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$defaults = array(
|
||||
'title' => __( 'Upcoming Events', 'jetpack' ),
|
||||
'feed-url' => '',
|
||||
'count' => 3,
|
||||
);
|
||||
$instance = array_merge( $defaults, (array) $instance );
|
||||
?>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'feed-url' ) ); ?>"><?php esc_html_e( 'iCalendar Feed URL:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'feed-url' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'feed-url' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['feed-url'] ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"><?php esc_html_e( 'Items to show:', 'jetpack' ); ?></label>
|
||||
<select id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>">
|
||||
<?php for ( $i = 1; $i <= 10; $i++ ) { ?>
|
||||
<option <?php selected( $instance['count'], $i ); ?>><?php echo (int) $i; ?></option>
|
||||
<?php } ?>
|
||||
<option value="0" <?php selected( $instance['count'], 0 ); ?>><?php esc_html_e( 'All', 'jetpack' ); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with the settings when they are saved by the admin.
|
||||
*
|
||||
* @param array $new_instance New configuration values.
|
||||
* @param array $old_instance Old configuration values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$instance = array();
|
||||
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
|
||||
$instance['feed-url'] = wp_strip_all_tags( $new_instance['feed-url'] );
|
||||
$instance['count'] = min( absint( $new_instance['count'] ), 10 ); // 10 or less
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the HTML for this widget.
|
||||
*
|
||||
* @param array $args An array of standard parameters for widgets in this theme.
|
||||
* @param array $instance An array of settings for this widget instance.
|
||||
*
|
||||
* @return void Echoes it's output
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
require_once JETPACK__PLUGIN_DIR . '/_inc/lib/icalendar-reader.php';
|
||||
|
||||
$ical = new iCalendarReader();
|
||||
$events = array();
|
||||
if ( ! empty( $instance['feed-url'] ) ) {
|
||||
$events = $ical->get_events( $instance['feed-url'], $instance['count'] );
|
||||
$events = $this->apply_timezone_offset( $events );
|
||||
}
|
||||
$ical->timezone = null;
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( ! empty( $instance['title'] ) ) {
|
||||
echo $args['before_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo esc_html( $instance['title'] );
|
||||
echo $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( empty( $instance['feed-url'] ) ) {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
echo '<div class="error-message">';
|
||||
esc_html_e( 'The events feed URL is not properly set up in this widget.', 'jetpack' );
|
||||
echo '</div>';
|
||||
}
|
||||
} elseif ( ! $events ) {
|
||||
echo '<p>';
|
||||
esc_html_e( 'No upcoming events', 'jetpack' );
|
||||
echo '</p>';
|
||||
} else {
|
||||
?>
|
||||
<ul class="upcoming-events">
|
||||
<?php foreach ( $events as $event ) : ?>
|
||||
<li>
|
||||
<strong class="event-summary">
|
||||
<?php
|
||||
echo $ical->escape( stripslashes( $event['SUMMARY'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
|
||||
?>
|
||||
</strong>
|
||||
<span class="event-when"><?php echo esc_html( $ical->formatted_date( $event ) ); ?></span>
|
||||
<?php if ( ! empty( $event['LOCATION'] ) ) : ?>
|
||||
<span class="event-location">
|
||||
<?php
|
||||
echo $ical->escape( stripslashes( $event['LOCATION'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
|
||||
?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if ( ! empty( $event['DESCRIPTION'] ) ) : ?>
|
||||
<span class="event-description">
|
||||
<?php
|
||||
echo wp_trim_words( $ical->escape( stripcslashes( $event['DESCRIPTION'] ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- this method is built to escape.
|
||||
?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'upcoming_events' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Left this function here for backward compatibility
|
||||
* just incase a site using jetpack is also using this function
|
||||
*
|
||||
* @param array|false $events Array of events, false on failure.
|
||||
*/
|
||||
private function apply_timezone_offset( $events ) {
|
||||
require_once JETPACK__PLUGIN_DIR . '/_inc/lib/icalendar-reader.php';
|
||||
|
||||
return ( new iCalendarReader() )->apply_timezone_offset( $events );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Display Recent WordPress Posts Widget
|
||||
* Description: Displays recent posts from a WordPress.com or Jetpack-enabled self-hosted WordPress site.
|
||||
* Version: 1.0
|
||||
* Author: Brad Angelcyk, Kathryn Presner, Justin Shreve, Carolyn Sonnek
|
||||
* Author URI: https://automattic.com
|
||||
* License: GPL2
|
||||
* Text Domain: jetpack
|
||||
*
|
||||
* @package automattic/jetpack
|
||||
*/
|
||||
|
||||
/**
|
||||
* Disable direct access/execution to/of the widget code.
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
require __DIR__ . '/wordpress-post-widget/class.jetpack-display-posts-widget-base.php';
|
||||
require __DIR__ . '/wordpress-post-widget/class.jetpack-display-posts-widget.php';
|
||||
|
||||
add_action( 'widgets_init', 'jetpack_display_posts_widget' );
|
||||
/**
|
||||
* Registers widget Jetpack_Display_Posts_Widget
|
||||
*/
|
||||
function jetpack_display_posts_widget() {
|
||||
register_widget( 'Jetpack_Display_Posts_Widget' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron tasks
|
||||
*/
|
||||
|
||||
add_filter( 'cron_schedules', 'jetpack_display_posts_widget_cron_intervals' ); // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval
|
||||
|
||||
/**
|
||||
* Adds 10 minute running interval to the cron schedules.
|
||||
*
|
||||
* @param array $current_schedules Currently defined schedules list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function jetpack_display_posts_widget_cron_intervals( $current_schedules ) {
|
||||
|
||||
/**
|
||||
* Only add the 10 minute interval if it wasn't already set.
|
||||
*/
|
||||
if ( ! isset( $current_schedules['minutes_10'] ) ) {
|
||||
$current_schedules['minutes_10'] = array(
|
||||
'interval' => 10 * MINUTE_IN_SECONDS,
|
||||
'display' => 'Every 10 minutes',
|
||||
);
|
||||
}
|
||||
|
||||
return $current_schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the cron task
|
||||
*/
|
||||
add_action( 'jetpack_display_posts_widget_cron_update', 'jetpack_display_posts_update_cron_action' );
|
||||
/**
|
||||
* Run the Jetpack_Display_Posts_Widget cron task.
|
||||
*/
|
||||
function jetpack_display_posts_update_cron_action() {
|
||||
$widget = new Jetpack_Display_Posts_Widget();
|
||||
$widget->cron_task();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle activation procedures for the cron.
|
||||
*
|
||||
* `updating_jetpack_version` - Handle cron activation when Jetpack gets updated. It's here
|
||||
* to cover the first cron activation after the update.
|
||||
*
|
||||
* `jetpack_activate_module_widgets` - Activate the cron when the Extra Sidebar widgets are activated.
|
||||
*
|
||||
* `activated_plugin` - Activate the cron when Jetpack gets activated.
|
||||
*/
|
||||
add_action( 'updating_jetpack_version', 'jetpack_display_posts_widget_conditionally_activate_cron' );
|
||||
add_action( 'jetpack_activate_module_widgets', 'Jetpack_Display_Posts_Widget::activate_cron' );
|
||||
add_action( 'activated_plugin', 'jetpack_conditionally_activate_cron_on_plugin_activation' );
|
||||
|
||||
/**
|
||||
* Executed when Jetpack gets activated. Tries to activate the cron if it is needed.
|
||||
*
|
||||
* @param string $plugin_file_name The plugin file that was activated.
|
||||
*/
|
||||
function jetpack_conditionally_activate_cron_on_plugin_activation( $plugin_file_name ) {
|
||||
if ( plugin_basename( JETPACK__PLUGIN_FILE ) === $plugin_file_name ) {
|
||||
jetpack_display_posts_widget_conditionally_activate_cron();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the cron only when needed.
|
||||
*
|
||||
* @see Jetpack_Display_Posts_Widget::should_cron_be_running
|
||||
*/
|
||||
function jetpack_display_posts_widget_conditionally_activate_cron() {
|
||||
$widget = new Jetpack_Display_Posts_Widget();
|
||||
if ( $widget->should_cron_be_running() ) {
|
||||
$widget->activate_cron();
|
||||
}
|
||||
|
||||
unset( $widget );
|
||||
}
|
||||
|
||||
/**
|
||||
* End of cron activation handling.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle deactivation procedures where they are needed.
|
||||
*
|
||||
* If Extra Sidebar Widgets module is deactivated, the cron is not needed.
|
||||
*
|
||||
* If Jetpack is deactivated, the cron is not needed.
|
||||
*/
|
||||
add_action( 'jetpack_deactivate_module_widgets', 'Jetpack_Display_Posts_Widget::deactivate_cron_static' );
|
||||
register_deactivation_hook( plugin_basename( JETPACK__PLUGIN_FILE ), 'Jetpack_Display_Posts_Widget::deactivate_cron_static' );
|
||||
+846
@@ -0,0 +1,846 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
|
||||
|
||||
/**
|
||||
* For back-compat, the final widget class must be named
|
||||
* Jetpack_Display_Posts_Widget.
|
||||
*
|
||||
* For convenience, it's nice to have a widget class constructor with no
|
||||
* arguments. Otherwise, we have to register the widget with an instance
|
||||
* instead of a class name. This makes unregistering annoying.
|
||||
*
|
||||
* Both WordPress.com and Jetpack implement the final widget class by
|
||||
* extending this __Base class and adding data fetching and storage.
|
||||
*
|
||||
* This would be a bit cleaner with dependency injection, but we already
|
||||
* use mocking to test, so it's not a big win.
|
||||
*
|
||||
* That this widget is currently implemented as these two classes
|
||||
* is an implementation detail and should not be depended on :)
|
||||
*
|
||||
* phpcs:disable PEAR.NamingConventions.ValidClassName.Invalid
|
||||
*/
|
||||
abstract class Jetpack_Display_Posts_Widget__Base extends WP_Widget {
|
||||
// phpcs:enable PEAR.NamingConventions.ValidClassName.Invalid
|
||||
|
||||
/**
|
||||
* Remote service API URL prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $service_url = 'https://public-api.wordpress.com/rest/v1.1/';
|
||||
|
||||
/**
|
||||
* Jetpack_Display_Posts_Widget__Base constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
// Internal id.
|
||||
'jetpack_display_posts_widget',
|
||||
/** This filter is documented in modules/widgets/facebook-likebox.php */
|
||||
apply_filters( 'jetpack_widget_name', __( 'Display WordPress Posts', 'jetpack' ) ),
|
||||
array(
|
||||
'description' => __( 'Displays a list of recent posts from another WordPress.com or Jetpack-enabled blog.', 'jetpack' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_customize_preview() ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue CSS and JavaScript.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_style(
|
||||
'jetpack_display_posts_widget',
|
||||
plugins_url( 'style.css', __FILE__ ),
|
||||
array(),
|
||||
JETPACK__VERSION
|
||||
);
|
||||
}
|
||||
|
||||
// DATA STORE: Must implement.
|
||||
|
||||
/**
|
||||
* Gets blog data from the cache.
|
||||
*
|
||||
* @param string $site Site.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
abstract public function get_blog_data( $site );
|
||||
|
||||
/**
|
||||
* Update a widget instance.
|
||||
*
|
||||
* @param string $site The site to fetch the latest data for.
|
||||
*
|
||||
* @return array - the new data
|
||||
*/
|
||||
abstract public function update_instance( $site );
|
||||
|
||||
// WIDGET API.
|
||||
|
||||
/**
|
||||
* Set up the widget display on the front end.
|
||||
*
|
||||
* @param array $args Widget args.
|
||||
* @param array $instance Widget instance.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
/** This action is documented in modules/widgets/gravatar-profile.php */
|
||||
do_action( 'jetpack_stats_extra', 'widget_view', 'display_posts' );
|
||||
|
||||
// Enqueue front end assets.
|
||||
$this->enqueue_scripts();
|
||||
|
||||
$content = $args['before_widget'];
|
||||
|
||||
if ( empty( $instance['url'] ) ) {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
$content .= '<p>';
|
||||
/* Translators: the "Blog URL" field mentioned is the input field labeled as such in the widget form. */
|
||||
$content .= esc_html__( 'The Blog URL is not properly setup in the widget.', 'jetpack' );
|
||||
$content .= '</p>';
|
||||
}
|
||||
$content .= $args['after_widget'];
|
||||
|
||||
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->get_blog_data( $instance['url'] );
|
||||
// Check for errors.
|
||||
if ( is_wp_error( $data ) || empty( $data['site_info']['data'] ) ) {
|
||||
$content .= '<p>' . __( 'Cannot load blog information at this time.', 'jetpack' ) . '</p>';
|
||||
$content .= $args['after_widget'];
|
||||
|
||||
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$site_info = $data['site_info']['data'];
|
||||
|
||||
if ( ! empty( $instance['title'] ) ) {
|
||||
/** This filter is documented in core/src/wp-includes/default-widgets.php */
|
||||
$instance['title'] = apply_filters( 'widget_title', $instance['title'] );
|
||||
$content .= $args['before_title'] . $instance['title'] . ': ' . $site_info->name . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
$content .= $args['before_title'] . esc_html( $site_info->name ) . $args['after_title'];
|
||||
}
|
||||
|
||||
$content .= '<div class="jetpack-display-remote-posts">';
|
||||
|
||||
if ( is_wp_error( $data['posts']['data'] ) || empty( $data['posts']['data'] ) ) {
|
||||
$content .= '<p>' . __( 'Cannot load blog posts at this time.', 'jetpack' ) . '</p>';
|
||||
$content .= '</div><!-- .jetpack-display-remote-posts -->';
|
||||
$content .= $args['after_widget'];
|
||||
|
||||
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$posts_list = $data['posts']['data'];
|
||||
|
||||
/**
|
||||
* Show only as much posts as we need. If we have less than configured amount,
|
||||
* we must show only that much posts.
|
||||
*/
|
||||
$number_of_posts = min( $instance['number_of_posts'], is_countable( $posts_list ) ? count( $posts_list ) : 0 );
|
||||
|
||||
for ( $i = 0; $i < $number_of_posts; $i++ ) {
|
||||
$single_post = $posts_list[ $i ];
|
||||
$post_title = ( $single_post['title'] ) ? $single_post['title'] : '( No Title )';
|
||||
|
||||
$target = '';
|
||||
if ( isset( $instance['open_in_new_window'] ) && true === $instance['open_in_new_window'] ) {
|
||||
$target = ' target="_blank" rel="noopener"';
|
||||
}
|
||||
$content .= '<h4><a href="' . esc_url( $single_post['url'] ) . '"' . $target . '>' . esc_html( $post_title ) . '</a></h4>' . "\n";
|
||||
if ( ( true === $instance['featured_image'] ) && ( ! empty( $single_post['featured_image'] ) ) ) {
|
||||
$featured_image = $single_post['featured_image'];
|
||||
/**
|
||||
* Allows setting up custom Photon parameters to manipulate the image output in the Display Posts widget.
|
||||
*
|
||||
* @see https://developer.wordpress.com/docs/photon/
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param array $args Array of Photon Parameters.
|
||||
*/
|
||||
$image_params = apply_filters( 'jetpack_display_posts_widget_image_params', array() );
|
||||
$content .= '<a title="' . esc_attr( $post_title ) . '" href="' . esc_url( $single_post['url'] ) . '"' . $target . '><img src="' . Image_CDN_Core::cdn_url( $featured_image, $image_params ) . '" alt="' . esc_attr( $post_title ) . '"/></a>';
|
||||
}
|
||||
|
||||
if ( true === $instance['show_excerpts'] ) {
|
||||
$content .= $single_post['excerpt'];
|
||||
}
|
||||
}
|
||||
|
||||
$content .= '</div><!-- .jetpack-display-remote-posts -->';
|
||||
$content .= $args['after_widget'];
|
||||
|
||||
/**
|
||||
* Filter the WordPress Posts widget content.
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param string $content Widget content.
|
||||
*/
|
||||
echo apply_filters( 'jetpack_display_posts_widget_content', $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the widget administration form.
|
||||
*
|
||||
* @param array $instance Widget instance configuration.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
|
||||
/**
|
||||
* Initialize widget configuration variables.
|
||||
*/
|
||||
$title = ( isset( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts', 'jetpack' );
|
||||
$url = ( isset( $instance['url'] ) ) ? $instance['url'] : '';
|
||||
$number_of_posts = ( isset( $instance['number_of_posts'] ) ) ? $instance['number_of_posts'] : 5;
|
||||
$open_in_new_window = ( isset( $instance['open_in_new_window'] ) ) ? $instance['open_in_new_window'] : false;
|
||||
$featured_image = ( isset( $instance['featured_image'] ) ) ? $instance['featured_image'] : false;
|
||||
$show_excerpts = ( isset( $instance['show_excerpts'] ) ) ? $instance['show_excerpts'] : false;
|
||||
|
||||
/**
|
||||
* Check if the widget instance has errors available.
|
||||
*
|
||||
* Only do so if a URL is set.
|
||||
*/
|
||||
$update_errors = array();
|
||||
|
||||
if ( ! empty( $url ) ) {
|
||||
$data = $this->get_blog_data( $url );
|
||||
$update_errors = $this->extract_errors_from_blog_data( $data );
|
||||
}
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'url' ) ); ?>"><?php esc_html_e( 'Blog URL:', 'jetpack' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'url' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'url' ) ); ?>" type="text" value="<?php echo esc_attr( $url ); ?>" />
|
||||
<i>
|
||||
<?php esc_html_e( 'Enter a WordPress.com or Jetpack WordPress site URL.', 'jetpack' ); ?>
|
||||
</i>
|
||||
<?php
|
||||
/**
|
||||
* Show an error if the URL field was left empty.
|
||||
*
|
||||
* The error is shown only when the widget was already saved.
|
||||
*/
|
||||
if ( empty( $url ) && ! preg_match( '/__i__|%i%/', $this->id ) ) {
|
||||
?>
|
||||
<br />
|
||||
<i class="error-message"><?php esc_html_e( 'You must specify a valid blog URL!', 'jetpack' ); ?></i>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'number_of_posts' ) ); ?>"><?php esc_html_e( 'Number of Posts to Display:', 'jetpack' ); ?></label>
|
||||
<select name="<?php echo esc_attr( $this->get_field_name( 'number_of_posts' ) ); ?>">
|
||||
<?php
|
||||
for ( $i = 1; $i <= 10; $i++ ) {
|
||||
echo '<option value="' . esc_attr( $i ) . '" ' . selected( $number_of_posts, $i ) . '>' . esc_html( $i ) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'open_in_new_window' ) ); ?>"><?php esc_html_e( 'Open links in new window/tab:', 'jetpack' ); ?></label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'open_in_new_window' ) ); ?>" <?php checked( $open_in_new_window, 1 ); ?> />
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'featured_image' ) ); ?>"><?php esc_html_e( 'Show Featured Image:', 'jetpack' ); ?></label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'featured_image' ) ); ?>" <?php checked( $featured_image, 1 ); ?> />
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'show_excerpts' ) ); ?>"><?php esc_html_e( 'Show Excerpts:', 'jetpack' ); ?></label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'show_excerpts' ) ); ?>" <?php checked( $show_excerpts, 1 ); ?> />
|
||||
</p>
|
||||
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Show error messages.
|
||||
*/
|
||||
if ( ! empty( $update_errors['message'] ) ) {
|
||||
|
||||
/**
|
||||
* Prepare the error messages.
|
||||
*/
|
||||
|
||||
$where_message = '';
|
||||
switch ( $update_errors['where'] ) {
|
||||
case 'posts':
|
||||
$where_message .= __( 'An error occurred while downloading blog posts list', 'jetpack' );
|
||||
break;
|
||||
|
||||
/**
|
||||
* If something else, beside `posts` and `site_info` broke,
|
||||
* don't handle it and default to blog `information`,
|
||||
* as it is generic enough.
|
||||
*/
|
||||
case 'site_info':
|
||||
default:
|
||||
$where_message .= __( 'An error occurred while downloading blog information', 'jetpack' );
|
||||
break;
|
||||
}
|
||||
|
||||
?>
|
||||
<p class="error-message">
|
||||
<?php echo esc_html( $where_message ); ?>:
|
||||
<br />
|
||||
<i>
|
||||
<?php echo esc_html( $update_errors['message'] ); ?>
|
||||
<?php
|
||||
/**
|
||||
* If there is any debug - show it here.
|
||||
*/
|
||||
if ( ! empty( $update_errors['debug'] ) ) {
|
||||
?>
|
||||
<br />
|
||||
<br />
|
||||
<?php esc_html_e( 'Detailed information', 'jetpack' ); ?>:
|
||||
<br />
|
||||
<?php echo esc_html( $update_errors['debug'] ); ?>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</i>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget update function.
|
||||
*
|
||||
* @param array $new_instance New instance widget settings.
|
||||
* @param array $old_instance Old instance widget settings.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
|
||||
$instance = array();
|
||||
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? wp_strip_all_tags( $new_instance['title'] ) : '';
|
||||
$instance['url'] = ( ! empty( $new_instance['url'] ) ) ? wp_strip_all_tags( trim( $new_instance['url'] ) ) : '';
|
||||
$instance['url'] = preg_replace( '!^https?://!is', '', $instance['url'] );
|
||||
$instance['url'] = untrailingslashit( $instance['url'] );
|
||||
|
||||
/**
|
||||
* Check if the URL should be with or without the www prefix before saving.
|
||||
*/
|
||||
if ( ! empty( $instance['url'] ) ) {
|
||||
$blog_data = $this->fetch_blog_data( $instance['url'], array(), true );
|
||||
|
||||
if ( is_wp_error( $blog_data['site_info']['error'] ) && str_starts_with( $instance['url'], 'www.' ) ) {
|
||||
$blog_data = $this->fetch_blog_data( substr( $instance['url'], 4 ), array(), true );
|
||||
|
||||
if ( ! is_wp_error( $blog_data['site_info']['error'] ) ) {
|
||||
$instance['url'] = substr( $instance['url'], 4 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$instance['number_of_posts'] = ( ! empty( $new_instance['number_of_posts'] ) ) ? (int) $new_instance['number_of_posts'] : '';
|
||||
$instance['open_in_new_window'] = ( ! empty( $new_instance['open_in_new_window'] ) ) ? true : '';
|
||||
$instance['featured_image'] = ( ! empty( $new_instance['featured_image'] ) ) ? true : '';
|
||||
$instance['show_excerpts'] = ( ! empty( $new_instance['show_excerpts'] ) ) ? true : '';
|
||||
|
||||
/**
|
||||
* If there is no cache entry for the specified URL, run a forced update.
|
||||
*
|
||||
* @see get_blog_data Returns WP_Error if the cache is empty, which is what is needed here.
|
||||
*/
|
||||
$cached_data = $this->get_blog_data( $instance['url'] );
|
||||
|
||||
if ( is_wp_error( $cached_data ) ) {
|
||||
$this->update_instance( $instance['url'] );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
// DATA PROCESSING.
|
||||
|
||||
/**
|
||||
* Expiring transients have a name length maximum of 45 characters,
|
||||
* so this function returns an abbreviated MD5 hash to use instead of
|
||||
* the full URI.
|
||||
*
|
||||
* @param string $site Site to get the hash for.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_site_hash( $site ) {
|
||||
return substr( md5( $site ), 0, 21 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a remote service endpoint and parse it.
|
||||
*
|
||||
* Timeout is set to 15 seconds right now, because sometimes the WordPress API
|
||||
* takes more than 5 seconds to fully respond.
|
||||
*
|
||||
* Caching is used here so we can avoid re-downloading the same endpoint
|
||||
* in a single request.
|
||||
*
|
||||
* @param string $endpoint Parametrized endpoint to call.
|
||||
*
|
||||
* @param int $timeout How much time to wait for the API to respond before failing.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function fetch_service_endpoint( $endpoint, $timeout = 15 ) {
|
||||
|
||||
/**
|
||||
* Holds endpoint request cache.
|
||||
*/
|
||||
static $cache = array();
|
||||
|
||||
if ( ! isset( $cache[ $endpoint ] ) ) {
|
||||
$raw_data = $this->wp_wp_remote_get( $this->service_url . ltrim( $endpoint, '/' ), array( 'timeout' => $timeout ) );
|
||||
$cache[ $endpoint ] = $this->parse_service_response( $raw_data );
|
||||
}
|
||||
|
||||
return $cache[ $endpoint ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse data from service response.
|
||||
* Do basic error handling for general service and data errors
|
||||
*
|
||||
* @param array $service_response Response from the service.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function parse_service_response( $service_response ) {
|
||||
/**
|
||||
* If there is an error, we add the error message to the parsed response
|
||||
*/
|
||||
if ( is_wp_error( $service_response ) ) {
|
||||
return new WP_Error(
|
||||
'general_error',
|
||||
__( 'An error occurred fetching the remote data.', 'jetpack' ),
|
||||
$service_response->get_error_messages()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate HTTP response code.
|
||||
*/
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $service_response ) ) {
|
||||
return new WP_Error(
|
||||
'http_error',
|
||||
__( 'An error occurred fetching the remote data.', 'jetpack' ),
|
||||
wp_remote_retrieve_response_message( $service_response )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract service response body from the request.
|
||||
*/
|
||||
|
||||
$service_response_body = wp_remote_retrieve_body( $service_response );
|
||||
|
||||
/**
|
||||
* No body has been set in the response. This should be pretty bad.
|
||||
*/
|
||||
if ( ! $service_response_body ) {
|
||||
return new WP_Error(
|
||||
'no_body',
|
||||
__( 'Invalid remote response.', 'jetpack' ),
|
||||
'No body in response.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the JSON response from the API. Convert to associative array.
|
||||
*/
|
||||
$parsed_data = json_decode( $service_response_body );
|
||||
|
||||
/**
|
||||
* If there is a problem with parsing the posts return an empty array.
|
||||
*/
|
||||
if ( $parsed_data === null ) {
|
||||
return new WP_Error(
|
||||
'no_body',
|
||||
__( 'Invalid remote response.', 'jetpack' ),
|
||||
'Invalid JSON from remote.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors in the parsed body.
|
||||
*/
|
||||
if ( isset( $parsed_data->error ) ) {
|
||||
return new WP_Error(
|
||||
'remote_error',
|
||||
__( 'It looks like the WordPress site URL is incorrectly configured. Please check it in your widget settings.', 'jetpack' ),
|
||||
$parsed_data->error
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No errors found, return parsed data.
|
||||
*/
|
||||
return $parsed_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch site information from the WordPress public API
|
||||
*
|
||||
* @param string $site URL of the site to fetch the information for.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function fetch_site_info( $site ) {
|
||||
|
||||
$response = $this->fetch_service_endpoint( sprintf( '/sites/%s', rawurlencode( $site ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse external API response from the site info call and handle errors if they occur.
|
||||
*
|
||||
* @param array|WP_Error $service_response The raw response to be parsed.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function parse_site_info_response( $service_response ) {
|
||||
|
||||
/**
|
||||
* If the service returned an error, we pass it on.
|
||||
*/
|
||||
if ( is_wp_error( $service_response ) ) {
|
||||
return $service_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the service returned proper site information.
|
||||
*/
|
||||
if ( ! isset( $service_response->ID ) ) {
|
||||
return new WP_Error(
|
||||
'no_site_info',
|
||||
__( 'Invalid site information returned from remote.', 'jetpack' ),
|
||||
'No site ID present in the response.'
|
||||
);
|
||||
}
|
||||
|
||||
return $service_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch list of posts from the WordPress public API.
|
||||
*
|
||||
* @param int $site_id The site to fetch the posts for.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function fetch_posts_for_site( $site_id ) {
|
||||
|
||||
$response = $this->fetch_service_endpoint(
|
||||
sprintf(
|
||||
'/sites/%1$d/posts/%2$s',
|
||||
$site_id,
|
||||
/**
|
||||
* Filters the parameters used to fetch for posts in the Display Posts Widget.
|
||||
*
|
||||
* @see https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/
|
||||
*
|
||||
* @module widgets
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $args Extra parameters to filter posts returned from the WordPress.com REST API.
|
||||
*/
|
||||
apply_filters( 'jetpack_display_posts_widget_posts_params', '?fields=id,title,excerpt,URL,featured_image' )
|
||||
)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse external API response from the posts list request and handle errors if any occur.
|
||||
*
|
||||
* @param object|WP_Error $service_response The raw response to be parsed.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function parse_posts_response( $service_response ) {
|
||||
|
||||
/**
|
||||
* If the service returned an error, we pass it on.
|
||||
*/
|
||||
if ( is_wp_error( $service_response ) ) {
|
||||
return $service_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the service returned proper posts array.
|
||||
*/
|
||||
if ( ! isset( $service_response->posts ) || ! is_array( $service_response->posts ) ) {
|
||||
return new WP_Error(
|
||||
'no_posts',
|
||||
__( 'No posts data returned by remote.', 'jetpack' ),
|
||||
'No posts information set in the returned data.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the posts to preserve storage space.
|
||||
*/
|
||||
|
||||
return $this->format_posts_for_storage( $service_response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the posts for better storage. Drop all the data that is not used.
|
||||
*
|
||||
* @param object $parsed_data Array of posts returned by the APIs.
|
||||
*
|
||||
* @return array Formatted posts or an empty array if no posts were found.
|
||||
*/
|
||||
public function format_posts_for_storage( $parsed_data ) {
|
||||
|
||||
$formatted_posts = array();
|
||||
|
||||
/**
|
||||
* Only go through the posts list if we have valid posts array.
|
||||
*/
|
||||
if ( isset( $parsed_data->posts ) && is_array( $parsed_data->posts ) ) {
|
||||
|
||||
/**
|
||||
* Loop through all the posts and format them appropriately.
|
||||
*/
|
||||
foreach ( $parsed_data->posts as $single_post ) {
|
||||
|
||||
$prepared_post = array(
|
||||
'title' => $single_post->title ? $single_post->title : '',
|
||||
'excerpt' => $single_post->excerpt ? $single_post->excerpt : '',
|
||||
'featured_image' => $single_post->featured_image ? $single_post->featured_image : '',
|
||||
'url' => $single_post->URL, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
);
|
||||
|
||||
/**
|
||||
* Append the formatted post to the results.
|
||||
*/
|
||||
$formatted_posts[] = $prepared_post;
|
||||
}
|
||||
}
|
||||
|
||||
return $formatted_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch site information and posts list for a site.
|
||||
*
|
||||
* @param string $site Site to fetch the data for.
|
||||
* @param array $original_data Optional original data to updated.
|
||||
*
|
||||
* @param bool $site_data_only Fetch only site information, skip posts list.
|
||||
*
|
||||
* @return array Updated or new data.
|
||||
*/
|
||||
public function fetch_blog_data( $site, $original_data = array(), $site_data_only = false ) {
|
||||
|
||||
/**
|
||||
* If no optional data is supplied, initialize a new structure
|
||||
*/
|
||||
if ( ! empty( $original_data ) ) {
|
||||
$widget_data = $original_data;
|
||||
} else {
|
||||
$widget_data = array(
|
||||
'site_info' => array(
|
||||
'last_check' => null,
|
||||
'last_update' => null,
|
||||
'error' => null,
|
||||
'data' => array(),
|
||||
),
|
||||
'posts' => array(
|
||||
'last_check' => null,
|
||||
'last_update' => null,
|
||||
'error' => null,
|
||||
'data' => array(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update check time and fetch site information.
|
||||
*/
|
||||
$widget_data['site_info']['last_check'] = time();
|
||||
|
||||
$site_info_raw_data = $this->fetch_site_info( $site );
|
||||
$site_info_parsed_data = $this->parse_site_info_response( $site_info_raw_data );
|
||||
|
||||
/**
|
||||
* If there is an error with the fetched site info, save the error and update the checked time.
|
||||
*/
|
||||
if ( is_wp_error( $site_info_parsed_data ) ) {
|
||||
$widget_data['site_info']['error'] = $site_info_parsed_data;
|
||||
|
||||
return $widget_data;
|
||||
} else {
|
||||
/**
|
||||
* If data is fetched successfully, update the data and set the proper time.
|
||||
*
|
||||
* Data is only updated if we have valid results. This is done this way so we can show
|
||||
* something if external service is down.
|
||||
*/
|
||||
$widget_data['site_info']['last_update'] = time();
|
||||
$widget_data['site_info']['data'] = $site_info_parsed_data;
|
||||
$widget_data['site_info']['error'] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If only site data is needed, return it here, don't fetch posts data.
|
||||
*/
|
||||
if ( true === $site_data_only ) {
|
||||
return $widget_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update check time and fetch posts list.
|
||||
*/
|
||||
$widget_data['posts']['last_check'] = time();
|
||||
|
||||
$site_posts_raw_data = $this->fetch_posts_for_site( $site_info_parsed_data->ID );
|
||||
$site_posts_parsed_data = $this->parse_posts_response( $site_posts_raw_data );
|
||||
|
||||
/**
|
||||
* If there is an error with the fetched posts, save the error and update the checked time.
|
||||
*/
|
||||
if ( is_wp_error( $site_posts_parsed_data ) ) {
|
||||
$widget_data['posts']['error'] = $site_posts_parsed_data;
|
||||
|
||||
return $widget_data;
|
||||
} else {
|
||||
/**
|
||||
* If data is fetched successfully, update the data and set the proper time.
|
||||
*
|
||||
* Data is only updated if we have valid results. This is done this way so we can show
|
||||
* something if external service is down.
|
||||
*/
|
||||
$widget_data['posts']['last_update'] = time();
|
||||
$widget_data['posts']['data'] = $site_posts_parsed_data;
|
||||
$widget_data['posts']['error'] = null;
|
||||
}
|
||||
|
||||
return $widget_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan and extract first error from blog data array.
|
||||
*
|
||||
* @param array|WP_Error $blog_data Blog data to scan for errors.
|
||||
*
|
||||
* @return string First error message found
|
||||
*/
|
||||
public function extract_errors_from_blog_data( $blog_data ) {
|
||||
|
||||
$errors = array(
|
||||
'message' => '',
|
||||
'debug' => '',
|
||||
'where' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* When the cache result is an error. Usually when the cache is empty.
|
||||
* This is not an error case for now.
|
||||
*/
|
||||
if ( is_wp_error( $blog_data ) ) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through `site_info` and `posts` keys of $blog_data.
|
||||
*/
|
||||
foreach ( array( 'site_info', 'posts' ) as $info_key ) {
|
||||
|
||||
/**
|
||||
* Contains information on which stage the error ocurred.
|
||||
*/
|
||||
$errors['where'] = $info_key;
|
||||
|
||||
/**
|
||||
* If an error is set, we want to check it for usable messages.
|
||||
*/
|
||||
if ( isset( $blog_data[ $info_key ]['error'] ) && ! empty( $blog_data[ $info_key ]['error'] ) ) {
|
||||
|
||||
/**
|
||||
* Extract error message from the error, if possible.
|
||||
*/
|
||||
if ( is_wp_error( $blog_data[ $info_key ]['error'] ) ) {
|
||||
/**
|
||||
* In the case of WP_Error we want to have the error message
|
||||
* and the debug information available.
|
||||
*/
|
||||
$error_messages = $blog_data[ $info_key ]['error']->get_error_messages();
|
||||
$errors['message'] = reset( $error_messages );
|
||||
|
||||
$extra_data = $blog_data[ $info_key ]['error']->get_error_data();
|
||||
if ( is_array( $extra_data ) ) {
|
||||
$errors['debug'] = implode( '; ', $extra_data );
|
||||
} else {
|
||||
$errors['debug'] = $extra_data;
|
||||
}
|
||||
|
||||
break;
|
||||
} elseif ( is_array( $blog_data[ $info_key ]['error'] ) ) {
|
||||
/**
|
||||
* In this case we don't have debug information, because
|
||||
* we have no way to know the format. The widget works with
|
||||
* WP_Error objects only.
|
||||
*/
|
||||
$errors['message'] = reset( $blog_data[ $info_key ]['error'] );
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* We do nothing if no usable error is found.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just to make method mocks in the unit tests easier.
|
||||
*
|
||||
* @param string $url The URL to fetch.
|
||||
* @param array $args Optional. Request arguments.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function wp_wp_remote_get( $url, $args = array() ) {
|
||||
return wp_remote_get( $url, $args );
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
|
||||
use Automattic\Jetpack\Status;
|
||||
|
||||
/**
|
||||
* Display a list of recent posts from a WordPress.com or Jetpack-enabled blog.
|
||||
*/
|
||||
class Jetpack_Display_Posts_Widget extends Jetpack_Display_Posts_Widget__Base {
|
||||
/**
|
||||
* Widget options key prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $widget_options_key_prefix = 'display_posts_site_data_';
|
||||
|
||||
/**
|
||||
* The name of the cron that will update widget data.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $cron_name = 'jetpack_display_posts_widget_cron_update';
|
||||
|
||||
// DATA STORE.
|
||||
|
||||
/**
|
||||
* Gets blog data from the cache.
|
||||
*
|
||||
* @param string $site Site.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_blog_data( $site ) {
|
||||
// Load from cache, if nothing return an error.
|
||||
$site_hash = $this->get_site_hash( $site );
|
||||
|
||||
$cached_data = $this->wp_get_option( $this->widget_options_key_prefix . $site_hash );
|
||||
|
||||
/**
|
||||
* If the cache is empty, return an empty_cache error.
|
||||
*/
|
||||
if ( false === $cached_data ) {
|
||||
return new WP_Error(
|
||||
'empty_cache',
|
||||
__( 'Information about this blog is currently being retrieved.', 'jetpack' )
|
||||
);
|
||||
}
|
||||
|
||||
return $cached_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a widget instance.
|
||||
*
|
||||
* @param string $site The site to fetch the latest data for.
|
||||
*
|
||||
* @return array - the new data
|
||||
*/
|
||||
public function update_instance( $site ) {
|
||||
|
||||
/**
|
||||
* Fetch current information for a site.
|
||||
*/
|
||||
$site_hash = $this->get_site_hash( $site );
|
||||
|
||||
$option_key = $this->widget_options_key_prefix . $site_hash;
|
||||
|
||||
$instance_data = $this->wp_get_option( $option_key );
|
||||
|
||||
/**
|
||||
* Fetch blog data and save it in $instance_data.
|
||||
*/
|
||||
$new_data = $this->fetch_blog_data( $site, $instance_data );
|
||||
|
||||
/**
|
||||
* If the option doesn't exist yet - create a new option
|
||||
*/
|
||||
if ( false === $instance_data ) {
|
||||
$this->wp_add_option( $option_key, $new_data );
|
||||
} else {
|
||||
$this->wp_update_option( $option_key, $new_data );
|
||||
}
|
||||
|
||||
return $new_data;
|
||||
}
|
||||
|
||||
// WIDGET API.
|
||||
|
||||
/**
|
||||
* Widget update function.
|
||||
*
|
||||
* @param array $new_instance New instance widget settings.
|
||||
* @param array $old_instance Old instance widget settings.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = parent::update( $new_instance, $old_instance );
|
||||
|
||||
/**
|
||||
* Forcefully activate the update cron when saving widget instance.
|
||||
*
|
||||
* So we can be sure that it will be running later.
|
||||
*/
|
||||
$this->activate_cron();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
// CRON.
|
||||
|
||||
/**
|
||||
* Activates widget update cron task.
|
||||
*/
|
||||
public static function activate_cron() {
|
||||
if ( ! wp_next_scheduled( self::$cron_name ) ) {
|
||||
wp_schedule_event( time(), 'minutes_10', self::$cron_name );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates widget update cron task.
|
||||
*
|
||||
* This is a wrapper over the static method as it provides some syntactic sugar.
|
||||
*/
|
||||
public function deactivate_cron() {
|
||||
self::deactivate_cron_static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates widget update cron task.
|
||||
*/
|
||||
public static function deactivate_cron_static() {
|
||||
$next_scheduled_time = wp_next_scheduled( self::$cron_name );
|
||||
wp_unschedule_event( $next_scheduled_time, self::$cron_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the update cron should be running and returns appropriate result.
|
||||
*
|
||||
* @return bool If the cron should be running or not.
|
||||
*/
|
||||
public function should_cron_be_running() {
|
||||
/**
|
||||
* The cron doesn't need to run empty loops.
|
||||
*/
|
||||
$widget_instances = $this->get_instances_sites();
|
||||
|
||||
if ( empty( $widget_instances ) || ! is_array( $widget_instances ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
|
||||
/**
|
||||
* If Jetpack is not active or in offline mode, we don't want to update widget data.
|
||||
*/
|
||||
if ( ! Jetpack::is_connection_ready() && ! ( new Status() )->is_offline_mode() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If Extra Sidebar Widgets module is not active, we don't need to update widget data.
|
||||
*/
|
||||
if ( ! Jetpack::is_module_active( 'widgets' ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If none of the above checks failed, then we definitely want to update widget data.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main cron code. Updates all instances of the widget.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function cron_task() {
|
||||
|
||||
/**
|
||||
* If the cron should not be running, disable it.
|
||||
*/
|
||||
if ( false === $this->should_cron_be_running() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$instances_to_update = $this->get_instances_sites();
|
||||
|
||||
/**
|
||||
* If no instances are found to be updated - stop.
|
||||
*/
|
||||
if ( empty( $instances_to_update ) || ! is_array( $instances_to_update ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( $instances_to_update as $site_url ) {
|
||||
$this->update_instance( $site_url );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of unique sites from all instances of the widget.
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function get_instances_sites() {
|
||||
|
||||
$widget_settings = $this->wp_get_option( 'widget_jetpack_display_posts_widget' );
|
||||
|
||||
/**
|
||||
* If the widget still hasn't been added anywhere, the config will not be present.
|
||||
*
|
||||
* In such case we don't want to continue execution.
|
||||
*/
|
||||
if ( false === $widget_settings || ! is_array( $widget_settings ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$urls = array();
|
||||
|
||||
foreach ( $widget_settings as $widget_instance_data ) {
|
||||
if ( isset( $widget_instance_data['url'] ) && ! empty( $widget_instance_data['url'] ) ) {
|
||||
$urls[] = $widget_instance_data['url'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure only unique URLs are returned.
|
||||
*/
|
||||
$urls = array_unique( $urls );
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
// MOCKABLES.
|
||||
|
||||
/**
|
||||
* This is just to make method mocks in the unit tests easier.
|
||||
*
|
||||
* @param string $param Option key to get.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function wp_get_option( $param ) {
|
||||
return get_option( $param );
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just to make method mocks in the unit tests easier.
|
||||
*
|
||||
* @param string $option_name Option name to be added.
|
||||
* @param mixed $option_value Option value.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function wp_add_option( $option_name, $option_value ) {
|
||||
return add_option( $option_name, $option_value );
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just to make method mocks in the unit tests easier.
|
||||
*
|
||||
* @param string $option_name Option name to be updated.
|
||||
* @param mixed $option_value Option value.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function wp_update_option( $option_name, $option_value ) {
|
||||
return update_option( $option_name, $option_value );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.jetpack-display-remote-posts {
|
||||
margin: 5px 0 20px 0;
|
||||
}
|
||||
|
||||
.jetpack-display-remote-posts h4 {
|
||||
margin: 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jetpack-display-remote-posts p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jetpack-display-remote-posts img {
|
||||
max-width: 100%;
|
||||
}
|
||||
Reference in New Issue
Block a user