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
wp-parsely / src / UI / class-recommended-widget.php

class-recommended-widget.php in Parse.ly 3.24.2, at src/UI/class-recommended-widget.php

335 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * UI: Recommended Widget class
4 *
5 * Provides a widget with Parse.ly recommended articles.
6 *
7 * @package Parsely
8 */
9
10 declare(strict_types=1);
11
12 namespace Parsely\UI;
13
14 use Parsely\Parsely;
15 use Parsely\Utils\Utils;
16 use WP_Widget;
17
18 use const Parsely\PARSELY_FILE;
19
20 /**
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 * }
33 */
34 final class Recommended_Widget extends WP_Widget {
35 /**
36 * Instance of Parsely class.
37 *
38 * @var Parsely
39 */
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 ) {
64 parent::__construct(
65 'Parsely_Recommended_Widget',
66 __( 'Parse.ly Recommended Widget', 'wp-parsely' ),
67 array(
68 'classname' => 'Recommended_Widget parsely-recommended-widget-hidden',
69 'description' => __( 'Display a list of post recommendations, personalized for a visitor or the current post.', 'wp-parsely' ),
70 )
71 );
72
73 $this->parsely = $parsely;
74 }
75
76 /**
77 * Gets the URL for the Recommendations API (GET /related).
78 *
79 * @since 2.5.0
80 * @since 3.24.2 Includes only the arguments that /related uses.
81 *
82 * @see https://docs.parse.ly/content-recommendations/
83 *
84 * @internal While this is a public method now, this should be moved to a new class.
85 *
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`.
92 * @param int $return_limit Number of records to retrieve; defaults to "10".
93 * @return string API URL.
94 */
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' );
98
99 $query_args = array(
100 'apikey' => $site_id,
101 'sort' => $sort,
102 'limit' => $return_limit,
103 );
104
105 if ( null !== $published_within && 0 !== $published_within ) {
106 $query_args['pub_date_start'] = $published_within . 'd';
107 }
108
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 );
114 }
115
116 /**
117 * This is the widget function.
118 *
119 * @param array<string, string> $args Widget Arguments.
120 * @param array<mixed> $widget_settings Values saved to the db.
121 */
122 public function widget( $args, $widget_settings ): void /* @phpstan-ignore-line */ {
123 if ( ! $this->site_id_and_secret_are_populated() ) {
124 return;
125 }
126
127 $instance = $this->get_widget_settings( $widget_settings );
128 $removed_title_esc = remove_filter( 'widget_title', 'esc_html' );
129
130 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
131 $title = apply_filters( 'widget_title', $instance['title'] );
132
133 if ( $removed_title_esc ) {
134 add_filter( 'widget_title', 'esc_html' );
135 }
136
137 $title_html = $args['before_widget'] . $args['before_title'] . $title . $args['after_title'];
138 echo wp_kses_post( $title_html );
139
140 // Set up the variables.
141 $api_url = $this->get_api_url(
142 $this->parsely->get_site_id(),
143 (int) $instance['published_within'], // @phpstan-ignore-line
144 $instance['sort'],
145 (int) $instance['return_limit'] // @phpstan-ignore-line
146 );
147
148 ?>
149
150 <div class="parsely-recommended-widget"
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'] ); ?>"
153 data-parsely-widget-api-url="<?php echo esc_url( $api_url ); ?>"
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 ); ?>"
158 ></div>
159
160 <?php
161
162 $recommended_widget_script_asset = Utils::get_asset_info( 'build/recommended-widget.asset.php' );
163
164 wp_register_script(
165 'wp-parsely-recommended-widget',
166 plugin_dir_url( PARSELY_FILE ) . 'build/recommended-widget.js',
167 $recommended_widget_script_asset['dependencies'],
168 $recommended_widget_script_asset['version'],
169 true
170 );
171
172 wp_register_style(
173 'wp-parsely-recommended-widget',
174 plugin_dir_url( PARSELY_FILE ) . 'build/recommended-widget.css',
175 array(),
176 $recommended_widget_script_asset['version']
177 );
178
179 wp_enqueue_script( 'wp-parsely-recommended-widget' );
180 wp_enqueue_style( 'wp-parsely-recommended-widget' );
181
182 echo wp_kses_post( $args['after_widget'] );
183 }
184
185 /**
186 * This is the form function.
187 *
188 * @param array<mixed> $current_settings Values saved to the db.
189 */
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' );
193
194 $message = sprintf(
195 /* translators: %s: Plugin settings page URL */
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' ),
197 esc_url( $settings_page_url )
198 );
199
200 echo '<p>', wp_kses_post( $message ), '</p>';
201
202 return '';
203 }
204
205 $instance = $this->get_widget_settings( $current_settings );
206
207 // editable fields: title.
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'];
216
217 $instance['return_limit'] = $return_limit;
218 $instance['display_direction'] = $display_direction;
219 $instance['published_within'] = $published_within;
220 $instance['sort'] = $sort;
221 $instance['personalize_results'] = $personalize_results;
222 $instance['img_src'] = $img_src;
223 $instance['display_author'] = $display_author;
224
225 ?>
226 <p>
227 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'wp-parsely' ); ?></label>
228 <br>
229 <input type="text" 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( $title ); ?>" class="widefat" />
230 </p>
231 <p>
232 <label for="<?php echo esc_attr( $this->get_field_id( 'published_within' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'published_within_label' ) ); ?>"><?php esc_html_e( 'Published within', 'wp-parsely' ); ?></label>
233 <input type="number" id="<?php echo esc_attr( $this->get_field_id( 'published_within' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'published_within' ) ); ?>" value="<?php echo esc_attr( (string) $instance['published_within'] ); ?>" min="0" max="30"
234 class="tiny-text" aria-labelledby="<?php echo esc_attr( $this->get_field_id( 'published_within_label' ) ); ?> <?php echo esc_attr( $this->get_field_id( 'published_within' ) ); ?> <?php echo esc_attr( $this->get_field_id( 'published_within_unit' ) ); ?>" />
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>
236 </p>
237 <p>
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>
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" />
240 </p>
241 <p>
242 <fieldset>
243 <legend><?php esc_html_e( 'Display entries:', 'wp-parsely' ); ?></legend>
244 <p>
245 <input type="radio" id="<?php echo esc_attr( $this->get_field_id( 'display_direction_horizontal' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'display_direction' ) ); ?>"<?php checked( $instance['display_direction'], 'horizontal' ); ?> value="horizontal" />
246 <label for="<?php echo esc_attr( $this->get_field_id( 'display_direction_horizontal' ) ); ?>"><?php esc_html_e( 'Horizontally', 'wp-parsely' ); ?></label>
247 <br />
248 <input type="radio" id="<?php echo esc_attr( $this->get_field_id( 'display_direction_vertical' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'display_direction' ) ); ?>"<?php checked( $instance['display_direction'], 'vertical' ); ?> value="vertical" />
249 <label for="<?php echo esc_attr( $this->get_field_id( 'display_direction_vertical' ) ); ?>"><?php esc_html_e( 'Vertically', 'wp-parsely' ); ?></label>
250 </p>
251 </fieldset>
252 </p>
253 <p>
254 <label for="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>"><?php esc_html_e( 'Sort by:', 'wp-parsely' ); ?></label>
255 <br>
256 <select id="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>" class="widefat">
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>
259 </select>
260 </p>
261 <p>
262 <label for="<?php echo esc_attr( $this->get_field_id( 'img_src' ) ); ?>"><?php esc_html_e( 'Image source:', 'wp-parsely' ); ?></label>
263 <br>
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">
265 <option<?php selected( $instance['img_src'], 'parsely_thumb' ); ?> value="parsely_thumb"><?php esc_html_e( 'Parse.ly generated thumbnail (85x85px)', 'wp-parsely' ); ?></option>
266 <option<?php selected( $instance['img_src'], 'original' ); ?> value="original"><?php esc_html_e( 'Original image', 'wp-parsely' ); ?></option>
267 <option<?php selected( $instance['img_src'], 'none' ); ?> value="none"><?php esc_html_e( 'No image', 'wp-parsely' ); ?></option>
268 </select>
269 </p>
270 <p>
271 <input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'display_author' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'display_author' ) ); ?>" value="display_author"<?php checked( $instance['display_author'], 'display_author' ); ?> />
272 <label for="<?php echo esc_attr( $this->get_field_id( 'display_author' ) ); ?>"><?php esc_html_e( 'Display author', 'wp-parsely' ); ?></label>
273 <br />
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' ); ?> />
275 <label for="<?php echo esc_attr( $this->get_field_id( 'personalize_results' ) ); ?>"><?php esc_html_e( 'Personalize recommended results', 'wp-parsely' ); ?></label>
276 </p>
277 <?php
278
279 return '';
280 }
281
282 /**
283 * This is the update function.
284 *
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
288 */
289 public function update( $new_instance, $old_instance ) /* @phpstan-ignore-line */ {
290 $instance = $old_instance;
291 $instance['title'] = trim( wp_kses_post( $new_instance['title'] ) );
292 $instance['published_within'] = $new_instance['published_within'];
293 $instance['return_limit'] = $new_instance['return_limit'] <= 20 ? $new_instance['return_limit'] : 20;
294 $instance['display_direction'] = trim( $new_instance['display_direction'] );
295 $instance['sort'] = trim( $new_instance['sort'] );
296 $instance['display_author'] = $new_instance['display_author'];
297 $instance['personalize_results'] = $new_instance['personalize_results'];
298 $instance['img_src'] = trim( $new_instance['img_src'] );
299
300 return $instance;
301 }
302
303 /**
304 * Checks if both the Site ID and API secret settings are populated with
305 * non-empty values.
306 *
307 * @since 2.5.0
308 *
309 * @return bool True if Site ID and API Secret settings are set.
310 * False otherwise.
311 */
312 private function site_id_and_secret_are_populated(): bool {
313 return $this->parsely->site_id_is_set() && $this->parsely->api_secret_is_set();
314 }
315
316 /**
317 * Returns all widget settings by assigning defaults if a setting isn't present
318 *
319 * @since 3.7.0
320 *
321 * @param array<string, mixed> $settings Widget Options.
322 * @return Widget_Settings
323 */
324 public function get_widget_settings( array $settings ) {
325 /**
326 * Variable.
327 *
328 * @var Widget_Settings
329 */
330 $widget_settings = $settings;
331
332 return array_merge( self::$default_widget_settings, $widget_settings );
333 }
334 }
335