PluginProbe
Parse.ly / 2.5.1
Parse.ly v2.5.1
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 3.20.3 All 105 releases
wp-parsely / src / class-parsely-recommended-widget.php

class-parsely-recommended-widget.php in Parse.ly 2.5.1, at src/class-parsely-recommended-widget.php

352 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Recommended Widget file
4 *
5 * This provides a widget to put on a page, will have parsely recommended articles
6 *
7 * @category Components
8 * @package WordPress
9 * @subpackage Parse.ly
10 */
11
12 /**
13 * This is the class for the recommended widget
14 *
15 * @category Class
16 * @package Parsely_Recommended_Widget
17 */
18 class Parsely_Recommended_Widget extends WP_Widget {
19 /**
20 * This is the constructor function
21 *
22 * @category Function
23 * @package WordPress
24 * @subpackage Parse.ly
25 */
26 public function __construct() {
27 parent::__construct(
28 'Parsely_Recommended_Widget',
29 __( 'Parse.ly Recommended Widget', 'wp-parsely' ),
30 array(
31 'classname' => 'Parsely_Recommended_Widget',
32 'description' => __( 'Display a list of post recommendations, personalized for a visitor or the current post.', 'wp-parsely' ),
33 )
34 );
35 }
36
37 /**
38 * Get the URL for the Recommendation API (GET /related).
39 *
40 * @see https://www.parse.ly/help/api/recommendations#get-related
41 *
42 * @internal While this is a public method now, this should be moved to a new class.
43 *
44 * @since 2.5.0
45 *
46 * @param string $api_key Publisher Site ID (API key).
47 * @param int $published_within Publication filter start date; see https://www.parse.ly/help/api/time for
48 * formatting details. No restriction by default.
49 * @param string $sort What to sort the results by. There are currently 2 valid options: `score`, which
50 * will sort articles by overall relevance and `pub_date` which will sort results by
51 * their publication date. The default is `score`.
52 * @param string $boost Available for sort=score only. Sub-sort value to re-rank relevant posts that
53 * received high e.g. views; default is undefined.
54 * @param int $return_limit Number of records to retrieve; defaults to "10".
55 * @return string API URL.
56 */
57 public function get_api_url( $api_key, $published_within, $sort, $boost, $return_limit ) {
58 $related_api_endpoint = 'https://api.parsely.com/v2/related';
59
60 $query_args = array(
61 'apikey' => $api_key,
62 'sort' => $sort,
63 'limit' => $return_limit,
64 );
65
66 if ( 'score' === $sort && $boost !== 'no-boost' ) {
67 $query_args['boost'] = $boost;
68 }
69
70 if ( 0 !== (int) $published_within ) {
71 $query_args['pub_date_start'] = $published_within . 'd';
72 }
73
74 return add_query_arg( $query_args, $related_api_endpoint );
75 }
76
77 /**
78 * This is the widget function
79 *
80 * @category Function
81 * @package WordPress
82 * @subpackage Parse.ly
83 * @param array $args Widget Arguments.
84 * @param array $instance Values saved to the db.
85 */
86 public function widget( $args, $instance ) {
87 if ( ! $this->api_key_and_secret_are_populated() ) {
88 return;
89 }
90
91 $removed_title_esc = remove_filter( 'widget_title', 'esc_html' );
92
93 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
94 $title = apply_filters( 'widget_title', $instance['title'] );
95 $title_html = $args['before_widget'] . $args['before_title'] . $title . $args['after_title'];
96
97 if ( $removed_title_esc ) {
98 add_filter( 'widget_title', 'esc_html' );
99 }
100
101 wp_enqueue_style( 'wp-parsely-style' );
102 wp_enqueue_script( 'jquery' );
103
104 $title_html = $args['before_widget'] . $args['before_title'] . $title . $args['after_title'];
105 echo wp_kses_post( $title_html );
106
107 // Set up the variables.
108 $options = get_option( 'parsely' );
109 $api_url = $this->get_api_url(
110 $options['apikey'],
111 $instance['published_within'],
112 $instance['sort'],
113 $instance['boost'],
114 $instance['return_limit']
115 );
116
117 $reco_widget_script_asset = require PARSELY_PLUGIN_DIR . 'build/admin-page.asset.php';
118
119 wp_register_script(
120 'wp-parsely-recommended-widget',
121 PARSELY_PLUGIN_URL . 'build/recommended-widget.js',
122 $reco_widget_script_asset[ 'dependencies' ],
123 PARSELY::get_asset_cache_buster(),
124 true
125 );
126
127 wp_localize_script(
128 'wp-parsely-recommended-widget',
129 'wpParselyRecommended',
130 array(
131 'displayAuthor' => isset( $instance['display_author'] ) ? wp_json_encode( boolval( $instance['display_author'] ) ) : false,
132 'displayDirection' => isset( $instance['display_direction'] ) ? $instance['display_direction'] : null,
133 'apiUrl' => $api_url,
134 'imgSrc' => isset( $instance['img_src'] ) ? $instance['img_src'] : null,
135 'permalink' => get_permalink(),
136 'personalized' => isset( $instance['personalize_results'] ) ? boolval( $instance['personalize_results'] ): false,
137 'widgetId' => $this->id,
138 )
139 );
140
141 wp_enqueue_script( 'wp-parsely-recommended-widget' );
142
143 echo wp_kses_post( $args['after_widget'] );
144 }
145
146 /**
147 * Migrates previous display_options settings
148 *
149 * @category Function
150 * @package WordPress
151 * @subpackage Parse.ly
152 * @param array $instance Values saved to the db.
153 */
154 private function migrate_old_fields( $instance ) {
155 if ( ! empty( $instance['display_options'] ) && is_array( $instance['display_options'] ) ) {
156 if ( empty( $instance['img_src'] ) ) {
157 $instance['img_src'] = in_array( 'display_thumbnail', $instance['display_options'], true ) ? 'parsely_thumb' : 'none';
158 }
159
160 if ( empty( $instance['display_author'] ) ) {
161 $instance['display_author'] = in_array( 'display_author', $instance['display_options'], true );
162 }
163 }
164 }
165
166
167 /**
168 * This is the form function
169 *
170 * @category Function
171 * @package WordPress
172 * @subpackage Parse.ly
173 * @param array $instance Values saved to the db.
174 */
175 public function form( $instance ) {
176 $this->migrate_old_fields( $instance );
177
178 if ( ! $this->api_key_and_secret_are_populated() ) {
179 $settings_page_url = add_query_arg( 'page', 'parsely', get_admin_url() . 'options-general.php' );
180
181 $message = sprintf(
182 /* translators: %s: Plugin settings page URL */
183 __( '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' ),
184 esc_url( $settings_page_url )
185 );
186
187 echo '<p>', wp_kses_post( $message ), '</p>';
188
189 return;
190 }
191
192 // editable fields: title.
193 $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
194 $return_limit = ! empty( $instance['return_limit'] ) ? $instance['return_limit'] : 5;
195 $display_direction = ! empty( $instance['display_direction'] ) ? $instance['display_direction'] : 'vertical';
196 $published_within = ! empty( $instance['published_within'] ) ? $instance['published_within'] : 0;
197 $sort = ! empty( $instance['sort'] ) ? $instance['sort'] : 'score';
198 $boost = ! empty( $instance['boost'] ) ? $instance['boost'] : 'views';
199 $personalize_results = ! empty( $instance['personalize_results'] ) ? $instance['personalize_results'] : false;
200 $img_src = ! empty( $instance['img_src'] ) ? $instance['img_src'] : 'parsely_thumb';
201 $display_author = ! empty( $instance['display_author'] ) ? $instance['display_author'] : false;
202
203 $instance['return_limit'] = $return_limit;
204 $instance['display_direction'] = $display_direction;
205 $instance['published_within'] = $published_within;
206 $instance['sort'] = $sort;
207 $instance['boost'] = $boost;
208 $instance['personalize_results'] = $personalize_results;
209 $instance['img_src'] = $img_src;
210 $instance['display_author'] = $display_author;
211
212 $boost_params = $this->get_boost_params();
213 ?>
214 <p>
215 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'wp-parsely' ); ?></label>
216 <br>
217 <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" />
218 </p>
219 <p>
220 <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>
221 <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"
222 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' ) ); ?>" />
223 <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>
224 </p>
225 <p>
226 <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>
227 <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" />
228 </p>
229 <p>
230 <fieldset>
231 <legend><?php esc_html_e( 'Display entries:', 'wp-parsely' ); ?></legend>
232 <p>
233 <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" />
234 <label for="<?php echo esc_attr( $this->get_field_id( 'display_direction_horizontal' ) ); ?>"><?php esc_html_e( 'Horizontally', 'wp-parsely' ); ?></label>
235 <br />
236 <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" />
237 <label for="<?php echo esc_attr( $this->get_field_id( 'display_direction_vertical' ) ); ?>"><?php esc_html_e( 'Vertically', 'wp-parsely' ); ?></label>
238 </p>
239 </fieldset>
240 </p>
241 <p>
242 <label for="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>"><?php esc_html_e( 'Sort by:', 'wp-parsely' ); ?></label>
243 <br>
244 <select id="<?php echo esc_attr( $this->get_field_id( 'sort' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>" class="widefat">
245 <option<?php selected( $instance['sort'], 'score' ); ?> value="score"><?php esc_html_e( 'Score (relevancy, boostable)', 'wp-parsely' ); ?></option>
246 <option<?php selected( $instance['sort'], 'pub_date' ); ?> value="pub_date"><?php esc_html_e( 'Publish date (not boostable)', 'wp-parsely' ); ?></option>
247 </select>
248 </p>
249 <p>
250 <label for="<?php echo esc_attr( $this->get_field_id( 'boost' ) ); ?>"><?php esc_html_e( 'Boost by:', 'wp-parsely' ); ?></label>
251 <br>
252 <select id="<?php echo esc_attr( $this->get_field_id( 'boost' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'boost' ) ); ?>" class="widefat">
253 <?php foreach ( $boost_params as $boost_param => $description ) { ?>
254 <option<?php selected( $instance['boost'], $boost_param ); ?> value="<?php echo esc_attr( $boost_param ); ?>"><?php echo esc_html( $description ); ?></option>
255 <?php } ?>
256 </select>
257
258 </p>
259 <p>
260 <label for="<?php echo esc_attr( $this->get_field_id( 'img_src' ) ); ?>"><?php esc_html_e( 'Image source:', 'wp-parsely' ); ?></label>
261 <br>
262 <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">
263 <option<?php selected( $instance['img_src'], 'parsely_thumb' ); ?> value="parsely_thumb"><?php esc_html_e( 'Parse.ly generated thumbnail (85x85px)', 'wp-parsely' ); ?></option>
264 <option<?php selected( $instance['img_src'], 'original' ); ?> value="original"><?php esc_html_e( 'Original image', 'wp-parsely' ); ?></option>
265 <option<?php selected( $instance['img_src'], 'none' ); ?> value="none"><?php esc_html_e( 'No image', 'wp-parsely' ); ?></option>
266 </select>
267 </p>
268 <p>
269 <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' ); ?> />
270 <label for="<?php echo esc_attr( $this->get_field_id( 'display_author' ) ); ?>"><?php esc_html_e( 'Display author', 'wp-parsely' ); ?></label>
271 <br />
272 <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' ); ?> />
273 <label for="<?php echo esc_attr( $this->get_field_id( 'personalize_results' ) ); ?>"><?php esc_html_e( 'Personalize recommended results', 'wp-parsely' ); ?></label>
274 </p>
275
276
277
278 <?php
279 }
280
281 /**
282 * This is the update function
283 *
284 * @category Function
285 * @package WordPress
286 * @subpackage Parse.ly
287 * @param array $new_instance The new values for the db.
288 * @param array $old_instance Values saved to the db.
289 */
290 public function update( $new_instance, $old_instance ) {
291 $instance = $old_instance;
292 $instance['title'] = trim( wp_kses_post( $new_instance['title'] ) );
293 $instance['published_within'] = (int) trim( $new_instance['published_within'] );
294 $instance['return_limit'] = (int) $new_instance['return_limit'] <= 20 ? $new_instance['return_limit'] : '20';
295 $instance['display_direction'] = trim( $new_instance['display_direction'] );
296 $instance['sort'] = trim( $new_instance['sort'] );
297 $instance['boost'] = trim( $new_instance['boost'] );
298 $instance['display_author'] = $new_instance['display_author'];
299 $instance['personalize_results'] = $new_instance['personalize_results'];
300 $instance['img_src'] = trim( $new_instance['img_src'] );
301 return $instance;
302 }
303
304 private function get_boost_params() {
305 return array(
306 'no-boost' => __( 'No boost', 'wp-parsely' ),
307 'views' => __( 'Page views', 'wp-parsely' ),
308 'mobile_views' => __( 'Page views on mobile devices', 'wp-parsely' ),
309 'tablet_views' => __( 'Page views on tablet devices', 'wp-parsely' ),
310 'desktop_views' => __( 'Page views on desktop devices', 'wp-parsely' ),
311 'visitors' => __( 'Unique page visitors, total', 'wp-parsely' ),
312 'visitors_new' => __( 'New visitors', 'wp-parsely' ),
313 'visitors_returning' => __( 'Returning visitors', 'wp-parsely' ),
314 'engaged_minutes' => __( 'Total engagement time in minutes', 'wp-parsely' ),
315 'avg_engaged' => __( 'Engaged minutes spent by total visitors', 'wp-parsely' ),
316 'avg_engaged_new' => __( 'Average engaged minutes spent by new visitors', 'wp-parsely' ),
317 'avg_engaged_returning' => __( 'Average engaged minutes spent by returning visitors', 'wp-parsely' ),
318 'social_interactions' => __( 'Total for Facebook, Twitter, LinkedIn, and Pinterest', 'wp-parsely' ),
319 'fb_interactions' => __( 'Count of Facebook shares, likes, and comments', 'wp-parsely' ),
320 'tw_interactions' => __( 'Count of Twitter tweets and retweets', 'wp-parsely' ),
321 'li_interactions' => __( 'Count of LinkedIn social interactions', 'wp-parsely' ),
322 'pi_interactions' => __( 'Count of Pinterest pins', 'wp-parsely' ),
323 'social_referrals' => __( 'Page views where the referrer was any social network', 'wp-parsely' ),
324 'fb_referrals' => __( 'Page views where the referrer was facebook.com', 'wp-parsely' ),
325 'tw_referrals' => __( 'Page views where the referrer was twitter.com', 'wp-parsely' ),
326 'li_referrals' => __( 'Page views where the referrer was linkedin.com', 'wp-parsely' ),
327 'pi_referrals' => __( 'Page views where the referrer was pinterest.com', 'wp-parsely' ),
328 );
329 }
330
331 private function api_key_and_secret_are_populated() {
332 $options = get_option( 'parsely' );
333
334 // No options are saved, so API key is not available.
335 if ( ! is_array( $options ) ) {
336 return false;
337 }
338
339 // Parse.ly Site ID settings field is not populated.
340 if ( ! array_key_exists( 'apikey', $options ) || $options['apikey'] === '' ) {
341 return false;
342 }
343
344 // Parse.ly API Secret settings field is not populated.
345 if ( ! array_key_exists( 'api_secret', $options ) || $options['api_secret'] === '' ) {
346 return false;
347 }
348
349 return true;
350 }
351 }
352