PluginProbe
Parse.ly / 3.24.2
Parse.ly v3.24.2
3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 All 106 releases
← All changes | src/UI/class-recommended-widget.php +130 -131 3.2.0 → 3.24.2 View file →
@@ -1,13 +1,11 @@
1 1 <?php
2 2 /**
3 - * Recommended Widget file
3 + * UI: Recommended Widget class
4 4 *
5 - * This provides a widget to put on a page, will have parsely recommended articles
5 + * Provides a widget with Parse.ly recommended articles.
6 6 *
7 - * @category Components
8 - * @package WordPress
9 - * @subpackage Parse.ly
7 + * @package Parsely
10 8 */
11 9
12 10 declare(strict_types=1);
13 11
@@ -12,20 +10,58 @@
12 10 declare(strict_types=1);
13 11
14 12 namespace Parsely\UI;
15 13
14 +use Parsely\Parsely;
15 +use Parsely\Utils\Utils;
16 16 use WP_Widget;
17 17
18 18 use const Parsely\PARSELY_FILE;
19 19
20 20 /**
21 - * This is the class for the recommended widget.
21 + * Provides a widget with Parse.ly recommended articles.
22 + *
23 + * @phpstan-type Widget_Settings array{
24 + * title: string,
25 + * return_limit: int,
26 + * display_direction: string,
27 + * published_within: int,
28 + * sort: string,
29 + * personalize_results: bool,
30 + * img_src: string,
31 + * display_author: bool,
32 + * }
22 33 */
23 34 final class Recommended_Widget extends WP_Widget {
24 35 /**
25 - * This is the constructor function.
36 + * Instance of Parsely class.
37 + *
38 + * @var Parsely
26 39 */
27 - public function __construct() {
40 + private $parsely;
41 +
42 + /**
43 + * Default values of widget settings
44 + *
45 + * @var Widget_Settings
46 + */
47 + private static $default_widget_settings = array(
48 + 'title' => '',
49 + 'return_limit' => 5,
50 + 'display_direction' => 'vertical',
51 + 'published_within' => 0,
52 + 'sort' => 'score',
53 + 'personalize_results' => false,
54 + 'img_src' => 'parsely_thumb',
55 + 'display_author' => false,
56 + );
57 +
58 + /**
59 + * Constructor.
60 + *
61 + * @param Parsely $parsely Instance of Parsely class.
62 + */
63 + public function __construct( Parsely $parsely ) {
28 64 parent::__construct(
29 65 'Parsely_Recommended_Widget',
30 66 __( 'Parse.ly Recommended Widget', 'wp-parsely' ),
31 67 array(
@@ -32,62 +68,64 @@
32 68 'classname' => 'Recommended_Widget parsely-recommended-widget-hidden',
33 69 'description' => __( 'Display a list of post recommendations, personalized for a visitor or the current post.', 'wp-parsely' ),
34 70 )
35 71 );
72 +
73 + $this->parsely = $parsely;
36 74 }
37 75
38 76 /**
39 - * Get the URL for the Recommendations API (GET /related).
77 + * Gets the URL for the Recommendations API (GET /related).
40 78 *
41 - * @see https://www.parse.ly/help/api/recommendations#get-related
79 + * @since 2.5.0
80 + * @since 3.24.2 Includes only the arguments that /related uses.
42 81 *
82 + * @see https://docs.parse.ly/content-recommendations/
83 + *
43 84 * @internal While this is a public method now, this should be moved to a new class.
44 85 *
45 - * @since 2.5.0
46 - *
47 - * @param string $api_key Publisher Site ID (API key).
48 - * @param int|null $published_within Publication filter start date; see https://www.parse.ly/help/api/time for
49 - * formatting details. No restriction by default.
50 - * @param string|null $sort What to sort the results by. There are currently 2 valid options: `score`, which
51 - * will sort articles by overall relevance and `pub_date` which will sort results by
52 - * their publication date. The default is `score`.
53 - * @param string|null $boost Available for sort=score only. Sub-sort value to re-rank relevant posts that
54 - * received high e.g. views; default is undefined.
86 + * @param string $site_id Publisher Site ID.
87 + * @param int|null $published_within Publication filter start date; see https://docs.parse.ly/api-date-time/ for
88 + * formatting details. No restriction by default.
89 + * @param string|null $sort What to sort the results by. There are currently 2 valid options: `score`,
90 + * which will sort articles by overall relevance and `pub_date` which will sort
91 + * results by their publication date. The default is `score`.
55 92 * @param int $return_limit Number of records to retrieve; defaults to "10".
56 93 * @return string API URL.
57 94 */
58 - private function get_api_url( string $api_key, ?int $published_within, ?string $sort, ?string $boost, int $return_limit ): string {
59 - $related_api_endpoint = 'https://api.parsely.com/v2/related';
95 + private function get_api_url( string $site_id, ?int $published_within, ?string $sort, int $return_limit ): string {
96 + $content_api = $this->parsely->get_content_api();
97 + $related_api_endpoint = $content_api->get_endpoint( '/related' );
60 98
61 99 $query_args = array(
62 - 'apikey' => $api_key,
100 + 'apikey' => $site_id,
63 101 'sort' => $sort,
64 102 'limit' => $return_limit,
65 103 );
66 104
67 - if ( 'score' === $sort && 'no-boost' !== $boost ) {
68 - $query_args['boost'] = $boost;
69 - }
70 -
71 105 if ( null !== $published_within && 0 !== $published_within ) {
72 106 $query_args['pub_date_start'] = $published_within . 'd';
73 107 }
74 108
75 - return add_query_arg( $query_args, $related_api_endpoint );
109 + // Only the arguments that /related uses.
110 + return add_query_arg(
111 + $query_args,
112 + $content_api->get_api_url() . $related_api_endpoint->get_endpoint()
113 + );
76 114 }
77 115
78 116 /**
79 - * This is the widget function
117 + * This is the widget function.
80 118 *
81 - * @param array $args Widget Arguments.
82 - * @param array $instance Values saved to the db.
83 - * @return void
119 + * @param array<string, string> $args Widget Arguments.
120 + * @param array<mixed> $widget_settings Values saved to the db.
84 121 */
85 - public function widget( $args, $instance ): void {
86 - if ( ! $this->api_key_and_secret_are_populated() ) {
122 + public function widget( $args, $widget_settings ): void /* @phpstan-ignore-line */ {
123 + if ( ! $this->site_id_and_secret_are_populated() ) {
87 124 return;
88 125 }
89 126
127 + $instance = $this->get_widget_settings( $widget_settings );
90 128 $removed_title_esc = remove_filter( 'widget_title', 'esc_html' );
91 129
92 130 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
93 131 $title = apply_filters( 'widget_title', $instance['title'] );
@@ -99,33 +137,31 @@
99 137 $title_html = $args['before_widget'] . $args['before_title'] . $title . $args['after_title'];
100 138 echo wp_kses_post( $title_html );
101 139
102 140 // Set up the variables.
103 - $options = get_option( 'parsely' );
104 141 $api_url = $this->get_api_url(
105 - $options['apikey'],
106 - $instance['published_within'],
142 + $this->parsely->get_site_id(),
143 + (int) $instance['published_within'], // @phpstan-ignore-line
107 144 $instance['sort'],
108 - $instance['boost'],
109 - (int) $instance['return_limit']
145 + (int) $instance['return_limit'] // @phpstan-ignore-line
110 146 );
111 147
112 - $recommended_widget_script_asset = require plugin_dir_path( PARSELY_FILE ) . 'build/recommended-widget.asset.php';
113 -
114 148 ?>
115 149
116 150 <div class="parsely-recommended-widget"
117 - data-parsely-widget-display-author="<?php echo esc_attr( wp_json_encode( isset( $instance['display_author'] ) && $instance['display_author'] ) ); ?>"
118 - data-parsely-widget-display-direction="<?php echo esc_attr( $instance['display_direction'] ?? '' ); ?>"
151 + data-parsely-widget-display-author="<?php echo esc_attr( (string) wp_json_encode( $instance['display_author'] ) ); ?>"
152 + data-parsely-widget-display-direction="<?php echo esc_attr( $instance['display_direction'] ); ?>"
119 153 data-parsely-widget-api-url="<?php echo esc_url( $api_url ); ?>"
120 - data-parsely-widget-img-display="<?php echo esc_attr( $instance['img_src'] ?? '' ); ?>"
121 - data-parsely-widget-permalink="<?php echo esc_url( get_permalink() ); ?>"
122 - data-parsely-widget-personalized="<?php echo esc_attr( wp_json_encode( isset( $instance['personalize_results'] ) && $instance['personalize_results'] ) ); ?>"
123 - data-parsely-widget-id="<?php echo esc_attr( $this->id ); ?>"
154 + data-parsely-widget-img-display="<?php echo esc_attr( $instance['img_src'] ); ?>"
155 + data-parsely-widget-permalink="<?php echo esc_url( (string) get_permalink() ); ?>"
156 + data-parsely-widget-personalized="<?php echo esc_attr( (string) wp_json_encode( $instance['personalize_results'] ) ); ?>"
157 + data-parsely-widget-id="<?php echo esc_attr( (string) $this->id ); ?>"
124 158 ></div>
125 159
126 160 <?php
127 161
162 + $recommended_widget_script_asset = Utils::get_asset_info( 'build/recommended-widget.asset.php' );
163 +
128 164 wp_register_script(
129 165 'wp-parsely-recommended-widget',
130 166 plugin_dir_url( PARSELY_FILE ) . 'build/recommended-widget.js',
131 167 $recommended_widget_script_asset['dependencies'],
@@ -146,15 +182,15 @@
146 182 echo wp_kses_post( $args['after_widget'] );
147 183 }
148 184
149 185 /**
150 - * This is the form function
186 + * This is the form function.
151 187 *
152 - * @param array $instance Values saved to the db.
188 + * @param array<mixed> $current_settings Values saved to the db.
153 189 */
154 - public function form( $instance ): void {
155 - if ( ! $this->api_key_and_secret_are_populated() ) {
156 - $settings_page_url = add_query_arg( 'page', 'parsely', get_admin_url() . 'options-general.php' );
190 + public function form( $current_settings ): string {
191 + if ( ! $this->site_id_and_secret_are_populated() ) {
192 + $settings_page_url = add_query_arg( 'page', 'parsely-settings', get_admin_url() . 'admin.php' );
157 193
158 194 $message = sprintf(
159 195 /* translators: %s: Plugin settings page URL */
160 196 __( 'The <i>Parse.ly Site ID</i> and <i>Parse.ly API Secret</i> fields need to be populated on the <a href="%s">Parse.ly settings page</a> for this widget to work.', 'wp-parsely' ),
@@ -162,32 +198,31 @@
162 198 );
163 199
164 200 echo '<p>', wp_kses_post( $message ), '</p>';
165 201
166 - return;
202 + return '';
167 203 }
168 204
205 + $instance = $this->get_widget_settings( $current_settings );
206 +
169 207 // editable fields: title.
170 - $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
171 - $return_limit = ! empty( $instance['return_limit'] ) ? (int) $instance['return_limit'] : 5;
172 - $display_direction = ! empty( $instance['display_direction'] ) ? $instance['display_direction'] : 'vertical';
173 - $published_within = ! empty( $instance['published_within'] ) ? $instance['published_within'] : 0;
174 - $sort = ! empty( $instance['sort'] ) ? $instance['sort'] : 'score';
175 - $boost = ! empty( $instance['boost'] ) ? $instance['boost'] : 'views';
176 - $personalize_results = ! empty( $instance['personalize_results'] ) ? $instance['personalize_results'] : false;
177 - $img_src = ! empty( $instance['img_src'] ) ? $instance['img_src'] : 'parsely_thumb';
178 - $display_author = ! empty( $instance['display_author'] ) ? $instance['display_author'] : false;
208 + $title = $instance['title'];
209 + $return_limit = $instance['return_limit'];
210 + $display_direction = $instance['display_direction'];
211 + $published_within = $instance['published_within'];
212 + $sort = $instance['sort'];
213 + $personalize_results = $instance['personalize_results'];
214 + $img_src = $instance['img_src'];
215 + $display_author = $instance['display_author'];
179 216
180 217 $instance['return_limit'] = $return_limit;
181 218 $instance['display_direction'] = $display_direction;
182 219 $instance['published_within'] = $published_within;
183 220 $instance['sort'] = $sort;
184 - $instance['boost'] = $boost;
185 221 $instance['personalize_results'] = $personalize_results;
186 222 $instance['img_src'] = $img_src;
187 223 $instance['display_author'] = $display_author;
188 224
189 - $boost_params = $this->get_boost_params();
190 225 ?>
191 226 <p>
192 227 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'wp-parsely' ); ?></label>
193 228 <br>
@@ -200,9 +235,9 @@
200 235 <span id="<?php echo esc_attr( $this->get_field_id( 'published_within_unit' ) ); ?>"> <?php esc_html_e( 'days (0 for no limit).', 'wp-parsely' ); ?></span>
201 236 </p>
202 237 <p>
203 238 <label for="<?php echo esc_attr( $this->get_field_id( 'return_limit' ) ); ?>"><?php esc_html_e( 'Number of posts to show (max 20):', 'wp-parsely' ); ?></label>
204 - <input type="number" id="<?php echo esc_attr( $this->get_field_id( 'return_limit' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'return_limit' ) ); ?>" value="<?php echo esc_attr( $instance['return_limit'] ); ?>" min="1" max="20" class="tiny-text" />
239 + <input type="number" id="<?php echo esc_attr( $this->get_field_id( 'return_limit' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'return_limit' ) ); ?>" value="<?php echo esc_attr( (string) $instance['return_limit'] ); ?>" min="1" max="20" class="tiny-text" />
205 240 </p>
206 241 <p>
207 242 <fieldset>
208 243 <legend><?php esc_html_e( 'Display entries:', 'wp-parsely' ); ?></legend>
@@ -218,23 +253,13 @@
218 253 <p>
219 254 <label for="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>"><?php esc_html_e( 'Sort by:', 'wp-parsely' ); ?></label>
220 255 <br>
221 256 <select id="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>" class="widefat">
222 - <option<?php selected( $instance['sort'], 'score' ); ?> value="score"><?php esc_html_e( 'Score (relevancy, boostable)', 'wp-parsely' ); ?></option>
223 - <option<?php selected( $instance['sort'], 'pub_date' ); ?> value="pub_date"><?php esc_html_e( 'Publish date (not boostable)', 'wp-parsely' ); ?></option>
257 + <option<?php selected( $instance['sort'], 'score' ); ?> value="score"><?php esc_html_e( 'Score', 'wp-parsely' ); ?></option>
258 + <option<?php selected( $instance['sort'], 'pub_date' ); ?> value="pub_date"><?php esc_html_e( 'Publish date', 'wp-parsely' ); ?></option>
224 259 </select>
225 260 </p>
226 261 <p>
227 - <label for="<?php echo esc_attr( $this->get_field_id( 'boost' ) ); ?>"><?php esc_html_e( 'Boost by:', 'wp-parsely' ); ?></label>
228 - <br>
229 - <select id="<?php echo esc_attr( $this->get_field_id( 'boost' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'boost' ) ); ?>" class="widefat">
230 - <?php foreach ( $boost_params as $boost_param => $description ) { ?>
231 - <option<?php selected( $instance['boost'], $boost_param ); ?> value="<?php echo esc_attr( $boost_param ); ?>"><?php echo esc_html( $description ); ?></option>
232 - <?php } ?>
233 - </select>
234 -
235 - </p>
236 - <p>
237 262 <label for="<?php echo esc_attr( $this->get_field_id( 'img_src' ) ); ?>"><?php esc_html_e( 'Image source:', 'wp-parsely' ); ?></label>
238 263 <br>
239 264 <select id="<?php echo esc_attr( $this->get_field_id( 'img_src' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'img_src' ) ); ?>" class="widefat">
240 265 <option<?php selected( $instance['img_src'], 'parsely_thumb' ); ?> value="parsely_thumb"><?php esc_html_e( 'Parse.ly generated thumbnail (85x85px)', 'wp-parsely' ); ?></option>
@@ -249,87 +274,61 @@
249 274 <input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'personalize_results' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'personalize_results' ) ); ?>" value="personalize_results"<?php checked( $instance['personalize_results'], 'personalize_results' ); ?> />
250 275 <label for="<?php echo esc_attr( $this->get_field_id( 'personalize_results' ) ); ?>"><?php esc_html_e( 'Personalize recommended results', 'wp-parsely' ); ?></label>
251 276 </p>
252 277 <?php
278 +
279 + return '';
253 280 }
254 281
255 282 /**
256 - * This is the update function
283 + * This is the update function.
257 284 *
258 - * @param array $new_instance The new values for the db.
259 - * @param array $old_instance Values saved to the db.
260 - * @return array
285 + * @param Widget_Settings $new_instance The new values for the db.
286 + * @param Widget_Settings $old_instance Values saved to the db.
287 + * @return Widget_Settings
261 288 */
262 - public function update( $new_instance, $old_instance ): array {
289 + public function update( $new_instance, $old_instance ) /* @phpstan-ignore-line */ {
263 290 $instance = $old_instance;
264 291 $instance['title'] = trim( wp_kses_post( $new_instance['title'] ) );
265 - $instance['published_within'] = is_int( $new_instance['published_within'] ) ? $new_instance['published_within'] : (int) trim( $new_instance['published_within'] );
266 - $instance['return_limit'] = (int) $new_instance['return_limit'] <= 20 ? (int) $new_instance['return_limit'] : 20;
292 + $instance['published_within'] = $new_instance['published_within'];
293 + $instance['return_limit'] = $new_instance['return_limit'] <= 20 ? $new_instance['return_limit'] : 20;
267 294 $instance['display_direction'] = trim( $new_instance['display_direction'] );
268 295 $instance['sort'] = trim( $new_instance['sort'] );
269 - $instance['boost'] = trim( $new_instance['boost'] );
270 296 $instance['display_author'] = $new_instance['display_author'];
271 297 $instance['personalize_results'] = $new_instance['personalize_results'];
272 298 $instance['img_src'] = trim( $new_instance['img_src'] );
299 +
273 300 return $instance;
274 301 }
275 302
276 303 /**
277 - * Return the list of boost parameters, values and labels.
304 + * Checks if both the Site ID and API secret settings are populated with
305 + * non-empty values.
278 306 *
279 307 * @since 2.5.0
280 308 *
281 - * @return array<string, string> Boost parameters values and labels.
309 + * @return bool True if Site ID and API Secret settings are set.
310 + * False otherwise.
282 311 */
283 - private function get_boost_params(): array {
284 - return array(
285 - 'no-boost' => __( 'No boost', 'wp-parsely' ),
286 - 'views' => __( 'Page views', 'wp-parsely' ),
287 - 'mobile_views' => __( 'Page views on mobile devices', 'wp-parsely' ),
288 - 'tablet_views' => __( 'Page views on tablet devices', 'wp-parsely' ),
289 - 'desktop_views' => __( 'Page views on desktop devices', 'wp-parsely' ),
290 - 'visitors' => __( 'Unique page visitors, total', 'wp-parsely' ),
291 - 'visitors_new' => __( 'New visitors', 'wp-parsely' ),
292 - 'visitors_returning' => __( 'Returning visitors', 'wp-parsely' ),
293 - 'engaged_minutes' => __( 'Total engagement time in minutes', 'wp-parsely' ),
294 - 'avg_engaged' => __( 'Engaged minutes spent by total visitors', 'wp-parsely' ),
295 - 'avg_engaged_new' => __( 'Average engaged minutes spent by new visitors', 'wp-parsely' ),
296 - 'avg_engaged_returning' => __( 'Average engaged minutes spent by returning visitors', 'wp-parsely' ),
297 - 'social_interactions' => __( 'Total for Facebook, Twitter, LinkedIn, and Pinterest', 'wp-parsely' ),
298 - 'fb_interactions' => __( 'Count of Facebook shares, likes, and comments', 'wp-parsely' ),
299 - 'tw_interactions' => __( 'Count of Twitter tweets and retweets', 'wp-parsely' ),
300 - 'pi_interactions' => __( 'Count of Pinterest pins', 'wp-parsely' ),
301 - 'social_referrals' => __( 'Page views where the referrer was any social network', 'wp-parsely' ),
302 - 'fb_referrals' => __( 'Page views where the referrer was facebook.com', 'wp-parsely' ),
303 - 'tw_referrals' => __( 'Page views where the referrer was twitter.com', 'wp-parsely' ),
304 - 'pi_referrals' => __( 'Page views where the referrer was pinterest.com', 'wp-parsely' ),
305 - );
312 + private function site_id_and_secret_are_populated(): bool {
313 + return $this->parsely->site_id_is_set() && $this->parsely->api_secret_is_set();
306 314 }
307 315
308 316 /**
309 - * Check if both the API key and API secret settings are populated with non-empty values.
317 + * Returns all widget settings by assigning defaults if a setting isn't present
310 318 *
311 - * @since 2.5.0
319 + * @since 3.7.0
312 320 *
313 - * @return bool True if apikey and api_secret settings are not empty strings. False otherwise.
321 + * @param array<string, mixed> $settings Widget Options.
322 + * @return Widget_Settings
314 323 */
315 - private function api_key_and_secret_are_populated(): bool {
316 - $options = get_option( 'parsely' );
324 + public function get_widget_settings( array $settings ) {
325 + /**
326 + * Variable.
327 + *
328 + * @var Widget_Settings
329 + */
330 + $widget_settings = $settings;
317 331
318 - // No options are saved, so API key is not available.
319 - if ( ! is_array( $options ) ) {
320 - return false;
321 - }
322 -
323 - // Parse.ly Site ID settings field is not populated.
324 - if ( ! array_key_exists( 'apikey', $options ) || '' === $options['apikey'] ) {
325 - return false;
326 - }
327 -
328 - // Parse.ly API Secret settings field is not populated.
329 - if ( ! array_key_exists( 'api_secret', $options ) || '' === $options['api_secret'] ) {
330 - return false;
331 - }
332 -
333 - return true;
332 + return array_merge( self::$default_widget_settings, $widget_settings );
334 333 }
335 334 }