PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 3.2.1
WP Popular Posts v3.2.1
4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 5.0.0 5.0.1 5.0.2 5.1.0 5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.2.0 6.2.1 6.3.0 6.3.1 6.3.2 6.3.3 6.3.4 6.4.0 6.4.1 6.4.2 7.0.0 7.0.1 7.1.0 7.2.0 7.3.0 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4.0 trunk 2.3.7 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.5 4.0.6
wordpress-popular-posts / wordpress-popular-posts.php
wordpress-popular-posts Last commit date
js 11 years ago lang 11 years ago style 11 years ago views 11 years ago index.php 11 years ago no_thumb.jpg 11 years ago readme.txt 11 years ago uninstall.php 11 years ago wordpress-popular-posts.php 11 years ago
wordpress-popular-posts.php
3234 lines
1 <?php
2 /*
3 Plugin Name: WordPress Popular Posts
4 Plugin URI: http://wordpress.org/extend/plugins/wordpress-popular-posts
5 Description: WordPress Popular Posts is a highly customizable widget that displays the most popular posts on your blog
6 Version: 3.2.1
7 Author: Hector Cabrera
8 Author URI: http://cabrerahector.com
9 Author Email: hcabrerab@gmail.com
10 Text Domain: wordpress-popular-posts
11 Domain Path: /lang/
12 Network: false
13 License: GPLv2 or later
14 License URI: http://www.gnu.org/licenses/gpl-2.0.html
15
16 Copyright 2014 Hector Cabrera (hcabrerab@gmail.com)
17
18 This program is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License, version 2, as
20 published by the Free Software Foundation.
21
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 */
31
32 if ( !defined('ABSPATH') )
33 exit('Please do not load this file directly.');
34
35 /**
36 * WordPress Popular Posts class.
37 */
38 if ( !class_exists('WordpressPopularPosts') ) {
39
40 /**
41 * Register plugin's activation / deactivation functions
42 * @since 1.3
43 */
44 register_activation_hook( __FILE__, array( 'WordpressPopularPosts', 'activate' ) );
45 register_deactivation_hook( __FILE__, array( 'WordpressPopularPosts', 'deactivate' ) );
46
47 /**
48 * Add function to widgets_init that'll load WPP.
49 * @since 2.0
50 */
51 function load_wpp() {
52 register_widget( 'WordpressPopularPosts' );
53 }
54 add_action( 'widgets_init', 'load_wpp' );
55
56 class WordpressPopularPosts extends WP_Widget {
57
58 /**
59 * Plugin version, used for cache-busting of style and script file references.
60 *
61 * @since 1.3.0
62 * @var string
63 */
64 private $version = '3.2.1';
65
66 /**
67 * Plugin identifier.
68 *
69 * @since 3.0.0
70 * @var string
71 */
72 private $plugin_slug = 'wordpress-popular-posts';
73
74 /**
75 * Instance of this class.
76 *
77 * @since 3.0.0
78 * @var object
79 */
80 protected static $instance = NULL;
81
82 /**
83 * Slug of the plugin screen.
84 *
85 * @since 3.0.0
86 * @var string
87 */
88 protected $plugin_screen_hook_suffix = NULL;
89
90 /**
91 * Flag for singular pages.
92 *
93 * @since 3.1.2
94 * @var int
95 */
96 private $current_post_id = 0;
97
98 /**
99 * Plugin directory.
100 *
101 * @since 1.4.6
102 * @var string
103 */
104 private $plugin_dir = '';
105
106 /**
107 * Plugin uploads directory.
108 *
109 * @since 3.0.4
110 * @var array
111 */
112 private $uploads_dir = array();
113
114 /**
115 * Default thumbnail.
116 *
117 * @since 2.2.0
118 * @var string
119 */
120 private $default_thumbnail = '';
121
122 /**
123 * Flag to verify if thumbnails can be created or not.
124 *
125 * @since 1.4.6
126 * @var bool
127 */
128 private $thumbnailing = false;
129
130 /**
131 * Flag to verify if qTrans is present.
132 *
133 * @since 1.4.6
134 * @var bool
135 */
136 private $qTrans = false;
137
138 /**
139 * Default charset.
140 *
141 * @since 2.1.4
142 * @var string
143 */
144 private $charset = "UTF-8";
145
146 /**
147 * Plugin defaults.
148 *
149 * @since 2.3.3
150 * @var array
151 */
152 protected $defaults = array(
153 'title' => '',
154 'limit' => 10,
155 'range' => 'daily',
156 'freshness' => false,
157 'order_by' => 'views',
158 'post_type' => 'post,page',
159 'pid' => '',
160 'author' => '',
161 'cat' => '',
162 'shorten_title' => array(
163 'active' => false,
164 'length' => 25,
165 'words' => false
166 ),
167 'post-excerpt' => array(
168 'active' => false,
169 'length' => 55,
170 'keep_format' => false,
171 'words' => false
172 ),
173 'thumbnail' => array(
174 'active' => false,
175 'build' => 'manual',
176 'width' => 15,
177 'height' => 15,
178 'crop' => true
179 ),
180 'rating' => false,
181 'stats_tag' => array(
182 'comment_count' => false,
183 'views' => true,
184 'author' => false,
185 'date' => array(
186 'active' => false,
187 'format' => 'F j, Y'
188 ),
189 'category' => false
190 ),
191 'markup' => array(
192 'custom_html' => false,
193 'wpp-start' => '&lt;ul class="wpp-list"&gt;',
194 'wpp-end' => '&lt;/ul&gt;',
195 'post-html' => '&lt;li&gt;{thumb} {title} {stats}&lt;/li&gt;',
196 'post-start' => '&lt;li&gt;',
197 'post-end' => '&lt;/li&gt;',
198 'title-start' => '&lt;h2&gt;',
199 'title-end' => '&lt;/h2&gt;'
200 )
201 );
202
203 /**
204 * Admin page user settings defaults.
205 *
206 * @since 2.3.3
207 * @var array
208 */
209 protected $default_user_settings = array(
210 'stats' => array(
211 'order_by' => 'views',
212 'limit' => 10,
213 'post_type' => 'post,page',
214 'freshness' => false
215 ),
216 'tools' => array(
217 'ajax' => false,
218 'css' => true,
219 'link' => array(
220 'target' => '_self'
221 ),
222 'thumbnail' => array(
223 'source' => 'featured',
224 'field' => '',
225 'resize' => false,
226 'default' => ''
227 ),
228 'log' => array(
229 'level' => 1
230 ),
231 'cache' => array(
232 'active' => false,
233 'interval' => array(
234 'time' => 'hour',
235 'value' => 1
236 )
237 ),
238 'sampling' => array(
239 'active' => false,
240 'rate' => 100
241 )
242 )
243 );
244
245 /**
246 * Admin page user settings.
247 *
248 * @since 2.3.3
249 * @var array
250 */
251 private $user_settings = array();
252
253 /**
254 * Bots list.
255 *
256 * @since 3.0.0
257 * @var array
258 */
259 protected $botlist = array( 'bot', 'crawl', 'curl', 'facebookexternalhit', 'geturl', 'google', 'java', 'msn', 'perl', 'slurp', 'spider', 'sqworm', 'search', 'wget' );
260
261 /*--------------------------------------------------*/
262 /* Constructor
263 /*--------------------------------------------------*/
264
265 /**
266 * Initialize the widget by setting localization, filters, and administration functions.
267 *
268 * @since 1.0.0
269 */
270 public function __construct() {
271
272 // Load plugin text domain
273 add_action( 'init', array( $this, 'widget_textdomain' ) );
274
275 // Upgrade check
276 add_action( 'init', array( $this, 'upgrade_check' ) );
277
278 // Check location on template redirect
279 add_action( 'template_redirect', array( $this, 'is_single' ) );
280
281 // Hook fired when a new blog is activated on WP Multisite
282 add_action( 'wpmu_new_blog', array( $this, 'activate_new_site' ) );
283
284 // Notices check
285 add_action( 'admin_notices', array( $this, 'check_admin_notices' ) );
286
287 // Create the widget
288 parent::__construct(
289 'wpp',
290 'WordPress Popular Posts',
291 array(
292 'classname' => 'popular-posts',
293 'description' => __( 'The most Popular Posts on your blog.', $this->plugin_slug )
294 )
295 );
296
297 // Get user options
298 $this->user_settings = get_site_option('wpp_settings_config');
299 if ( !$this->user_settings ) {
300 add_site_option('wpp_settings_config', $this->default_user_settings);
301 $this->user_settings = $this->default_user_settings;
302 } else {
303 $this->user_settings = $this->__merge_array_r( $this->default_user_settings, $this->user_settings );
304 }
305
306 // Allow WP themers / coders to override data sampling status (active/inactive)
307 $this->user_settings['tools']['sampling']['active'] = apply_filters( 'wpp_data_sampling', $this->user_settings['tools']['sampling']['active'] );
308
309 // Add the options page and menu item.
310 add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );
311
312 // Register admin styles and scripts
313 add_action( 'admin_print_styles', array( $this, 'register_admin_styles' ) );
314 add_action( 'admin_enqueue_scripts', array( $this, 'register_admin_scripts' ) );
315 add_action( 'admin_init', array( $this, 'thickbox_setup' ) );
316
317 // Register site styles and scripts
318 if ( $this->user_settings['tools']['css'] )
319 add_action( 'wp_enqueue_scripts', array( $this, 'register_widget_styles' ) );
320 add_action( 'wp_enqueue_scripts', array( $this, 'register_widget_scripts' ) );
321
322 // Add plugin settings link
323 add_filter( 'plugin_action_links', array( $this, 'add_plugin_settings_link' ), 10, 2 );
324
325 // Set plugin directory
326 $this->plugin_dir = plugin_dir_url(__FILE__);
327
328 // Get blog charset
329 $this->charset = get_bloginfo('charset');
330
331 // Add ajax table truncation to wp_ajax_ hook
332 add_action('wp_ajax_wpp_clear_data', array( $this, 'clear_data' ));
333
334 // Add thumbnail cache truncation to wp_ajax_ hook
335 add_action('wp_ajax_wpp_clear_thumbnail', array( $this, 'clear_thumbnails' ));
336
337 // Add ajax hook for widget
338 add_action('wp_ajax_wpp_get_popular', array( $this, 'get_popular') );
339 add_action('wp_ajax_nopriv_wpp_get_popular', array( $this, 'get_popular') );
340
341 // Check if images can be created
342 if ( extension_loaded('ImageMagick') || (extension_loaded('GD') && function_exists('gd_info')) )
343 $this->thumbnailing = true;
344
345 // Set default thumbnail
346 $this->default_thumbnail = $this->plugin_dir . "no_thumb.jpg";
347 $this->default_user_settings['tools']['thumbnail']['default'] = $this->default_thumbnail;
348
349 if ( !empty($this->user_settings['tools']['thumbnail']['default']) )
350 $this->default_thumbnail = $this->user_settings['tools']['thumbnail']['default'];
351 else
352 $this->user_settings['tools']['thumbnail']['default'] = $this->default_thumbnail;
353
354 // Set uploads folder
355 $wp_upload_dir = wp_upload_dir();
356 $this->uploads_dir['basedir'] = $wp_upload_dir['basedir'] . "/" . $this->plugin_slug;
357 $this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'] . "/" . $this->plugin_slug;
358
359 if ( !is_dir($this->uploads_dir['basedir']) ) {
360 if ( !wp_mkdir_p($this->uploads_dir['basedir']) ) {
361 $this->uploads_dir['basedir'] = $wp_upload_dir['basedir'];
362 $this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'];
363 }
364 }
365
366 // qTrans plugin support
367 if ( function_exists('qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage') )
368 $this->qTrans = true;
369
370 // Remove post/page prefetching!
371 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
372 // Add the update hooks only if the logging conditions are met
373 if ( (0 == $this->user_settings['tools']['log']['level'] && !is_user_logged_in()) || (1 == $this->user_settings['tools']['log']['level']) || (2 == $this->user_settings['tools']['log']['level'] && is_user_logged_in()) ) {
374
375 $update_views = true;
376
377 if ( $this->user_settings['tools']['sampling']['active'] ) {
378 $update_views = false;
379
380 /*
381 * Data sampling algorithm
382 * Borrowed from http://stackoverflow.com/a/4762559
383 *
384 * @link http://stackoverflow.com/questions/4762527/what-is-the-best-way-to-count-page-views-in-php-mysql/4762559#comment21730393_4762559, http://stackoverflow.com/questions/4762527/what-is-the-best-way-to-count-page-views-in-php-mysql/4762559#comment26582397_4762559, http://stackoverflow.com/a/6005550
385 *
386 */
387
388 if ( 1 == mt_rand(1, $this->user_settings['tools']['sampling']['rate']) ) {
389 $update_views = true;
390 }
391 }
392
393 // Log views on page load via AJAX
394 if ( $update_views ) {
395 add_action( 'wp_head', array(&$this, 'print_ajax') );
396
397 // Register views from everyone and/or connected users
398 if ( 0 != $this->user_settings['tools']['log']['level'] )
399 add_action( 'wp_ajax_update_views_ajax', array($this, 'update_views_ajax') );
400 // Register views from everyone and/or visitors only
401 if ( 2 != $this->user_settings['tools']['log']['level'] )
402 add_action( 'wp_ajax_nopriv_update_views_ajax', array($this, 'update_views_ajax') );
403
404 }
405
406 }
407
408 // Add shortcode
409 add_shortcode('wpp', array(&$this, 'shortcode'));
410
411 // Enable data purging at midnight
412 add_action( 'wpp_cache_event', array($this, 'purge_data') );
413 if ( !wp_next_scheduled('wpp_cache_event') ) {
414 $tomorrow = time() + 86400;
415 $midnight = mktime(0, 0, 0,
416 date("m", $tomorrow),
417 date("d", $tomorrow),
418 date("Y", $tomorrow));
419 wp_schedule_event( $midnight, 'daily', 'wpp_cache_event' );
420 }
421
422 } // end constructor
423
424 /*--------------------------------------------------*/
425 /* Widget API Functions
426 /*--------------------------------------------------*/
427
428 /**
429 * Outputs the content of the widget.
430 *
431 * @since 1.0.0
432 * @param array args The array of form elements
433 * @param array instance The current instance of the widget
434 */
435 public function widget( $args, $instance ) {
436
437 $this->__debug($args);
438
439 /**
440 * @var String $name
441 * @var String $id
442 * @var String $description
443 * @var String $class
444 * @var String $before_widget
445 * @var String $after_widget
446 * @var String $before_title
447 * @var String $after_title
448 * @var String $widget_id
449 * @var String $widget_name
450 */
451 extract( $args, EXTR_SKIP );
452
453 $markup = ( $instance['markup']['custom_html'] || has_filter('wpp_custom_html') || has_filter('wpp_post') )
454 ? 'custom'
455 : 'regular';
456
457 echo "\n". "<!-- WordPress Popular Posts Plugin v{$this->version} [W] [{$instance['range']}] [{$instance['order_by']}] [{$markup}]" . ( !empty($instance['pid']) ? " [PID]" : "" ) . ( !empty($instance['cat']) ? " [CAT]" : "" ) . ( !empty($instance['author']) ? " [UID]" : "" ) . " -->" . "\n";
458
459 echo $before_widget . "\n";
460
461 // has user set a title?
462 if ( '' != $instance['title'] ) {
463
464 $title = apply_filters( 'widget_title', $instance['title'] );
465
466 if ($instance['markup']['custom_html'] && $instance['markup']['title-start'] != "" && $instance['markup']['title-end'] != "" ) {
467 echo htmlspecialchars_decode($instance['markup']['title-start'], ENT_QUOTES) . $title . htmlspecialchars_decode($instance['markup']['title-end'], ENT_QUOTES);
468 } else {
469 echo $before_title . $title . $after_title;
470 }
471 }
472
473 if ( $this->user_settings['tools']['ajax'] ) {
474 if ( empty($before_widget) || !preg_match('/id="[^"]*"/', $before_widget) ) {
475 ?>
476 <p><?php _e('Error: cannot ajaxify WordPress Popular Posts on this theme. It\'s missing the <em>id</em> attribute on before_widget (see <a href="http://codex.wordpress.org/Function_Reference/register_sidebar" target="_blank" rel="nofollow">register_sidebar</a> for more).', $this->plugin_slug ); ?></p>
477 <?php
478 } else {
479 ?>
480 <script type="text/javascript">//<![CDATA[
481 // jQuery is available, so proceed
482 if ( window.jQuery ) {
483
484 jQuery(document).ready(function($){
485 $.get('<?php echo admin_url('admin-ajax.php'); ?>', {
486 action: 'wpp_get_popular',
487 id: '<?php echo $this->number; ?>'
488 }, function(data){
489 $('#<?php echo $widget_id; ?>').append(data);
490 });
491 });
492
493 } else { // jQuery is not defined
494 if ( window.console && window.console.log )
495 window.console.log('WordPress Popular Posts: jQuery is not defined!');
496 }
497 //]]></script>
498 <?php
499 }
500 } else {
501 echo $this->__get_popular_posts( $instance );
502 }
503
504 echo $after_widget . "\n";
505 echo "<!-- End WordPress Popular Posts Plugin v{$this->version} -->"."\n";
506
507 } // end widget
508
509 /**
510 * Processes the widget's options to be saved.
511 *
512 * @since 1.0.0
513 * @param array new_instance The previous instance of values before the update.
514 * @param array old_instance The new instance of values to be generated via the update.
515 * @return array instance Updated instance.
516 */
517 public function update( $new_instance, $old_instance ) {
518
519 $instance = $old_instance;
520
521 $instance['title'] = htmlspecialchars( stripslashes_deep(strip_tags( $new_instance['title'] )), ENT_QUOTES );
522 $instance['limit'] = ( $this->__is_numeric($new_instance['limit']) && $new_instance['limit'] > 0 )
523 ? $new_instance['limit']
524 : 10;
525 $instance['range'] = $new_instance['range'];
526 $instance['order_by'] = $new_instance['order_by'];
527
528 // FILTERS
529 // user did not define the custom post type name, so we fall back to default
530 $instance['post_type'] = ( '' == $new_instance['post_type'] )
531 ? 'post,page'
532 : $new_instance['post_type'];
533
534 $instance['freshness'] = isset( $new_instance['freshness'] );
535
536 $instance['pid'] = implode(",", array_filter(explode(",", preg_replace( '|[^0-9,]|', '', $new_instance['pid'] ))));
537 $instance['cat'] = implode(",", array_filter(explode(",", preg_replace( '|[^0-9,-]|', '', $new_instance['cat'] ))));
538 $instance['author'] = implode(",", array_filter(explode(",", preg_replace( '|[^0-9,]|', '', $new_instance['uid'] ))));
539
540 $instance['shorten_title']['words'] = $new_instance['shorten_title-words'];
541 $instance['shorten_title']['active'] = isset( $new_instance['shorten_title-active'] );
542 $instance['shorten_title']['length'] = ( $this->__is_numeric($new_instance['shorten_title-length']) && $new_instance['shorten_title-length'] > 0 )
543 ? $new_instance['shorten_title-length']
544 : 25;
545
546 $instance['post-excerpt']['keep_format'] = isset( $new_instance['post-excerpt-format'] );
547 $instance['post-excerpt']['words'] = $new_instance['post-excerpt-words'];
548 $instance['post-excerpt']['active'] = isset( $new_instance['post-excerpt-active'] );
549 $instance['post-excerpt']['length'] = ( $this->__is_numeric($new_instance['post-excerpt-length']) && $new_instance['post-excerpt-length'] > 0 )
550 ? $new_instance['post-excerpt-length']
551 : 55;
552
553 $instance['thumbnail']['active'] = false;
554 $instance['thumbnail']['width'] = 15;
555 $instance['thumbnail']['height'] = 15;
556
557 // can create thumbnails
558 if ( $this->thumbnailing ) {
559
560 $instance['thumbnail']['active'] = isset( $new_instance['thumbnail-active'] );
561 $instance['thumbnail']['build'] = $new_instance['thumbnail-size-source'];
562
563 // Use predefined thumbnail sizes
564 if ( 'predefined' == $new_instance['thumbnail-size-source'] ) {
565 $size = $this->__get_image_sizes( $new_instance['thumbnail-size'] );
566 $instance['thumbnail']['width'] = $size['width'];
567 $instance['thumbnail']['height'] = $size['height'];
568 $instance['thumbnail']['crop'] = $size['crop'];
569 } // Set thumbnail size manually
570 else {
571 if ($this->__is_numeric($new_instance['thumbnail-width']) && $this->__is_numeric($new_instance['thumbnail-height'])) {
572 $instance['thumbnail']['width'] = $new_instance['thumbnail-width'];
573 $instance['thumbnail']['height'] = $new_instance['thumbnail-height'];
574 $instance['thumbnail']['crop'] = true;
575 }
576 }
577
578 }
579
580 $instance['rating'] = isset( $new_instance['rating'] );
581 $instance['stats_tag']['comment_count'] = isset( $new_instance['comment_count'] );
582 $instance['stats_tag']['views'] = isset( $new_instance['views'] );
583 $instance['stats_tag']['author'] = isset( $new_instance['author'] );
584 $instance['stats_tag']['date']['active'] = isset( $new_instance['date'] );
585 $instance['stats_tag']['date']['format'] = empty($new_instance['date_format'])
586 ? 'F j, Y'
587 : $new_instance['date_format'];
588
589 $instance['stats_tag']['category'] = isset( $new_instance['category'] );
590 $instance['markup']['custom_html'] = isset( $new_instance['custom_html'] );
591 $instance['markup']['wpp-start'] = empty($new_instance['wpp-start'])
592 ? htmlspecialchars( '<ul class="wpp-list">', ENT_QUOTES )
593 : htmlspecialchars( $new_instance['wpp-start'], ENT_QUOTES );
594
595 $instance['markup']['wpp-end'] = empty($new_instance['wpp-end'])
596 ? htmlspecialchars( '</ul>', ENT_QUOTES )
597 : htmlspecialchars( $new_instance['wpp-end'], ENT_QUOTES );
598
599 $instance['markup']['post-html'] = empty($new_instance['post-html'])
600 ? htmlspecialchars( '<li>{thumb} {title} {stats}</li>', ENT_QUOTES )
601 : htmlspecialchars( $new_instance['post-html'], ENT_QUOTES );
602
603 $instance['markup']['title-start'] = empty($new_instance['title-start'])
604 ? ''
605 : htmlspecialchars( $new_instance['title-start'], ENT_QUOTES );
606
607 $instance['markup']['title-end'] = empty($new_instance['title-end'])
608 ? '' :
609 htmlspecialchars( $new_instance['title-end'], ENT_QUOTES );
610
611 return $instance;
612
613 } // end widget
614
615 /**
616 * Generates the administration form for the widget.
617 *
618 * @since 1.0.0
619 * @param array instance The array of keys and values for the widget.
620 */
621 public function form( $instance ) {
622
623 // parse instance values
624 $instance = $this->__merge_array_r(
625 $this->defaults,
626 $instance
627 );
628
629 // Display the admin form
630 include( plugin_dir_path(__FILE__) . '/views/form.php' );
631
632 } // end form
633
634 /*--------------------------------------------------*/
635 /* Public methods
636 /*--------------------------------------------------*/
637
638 /**
639 * Loads the Widget's text domain for localization and translation.
640 *
641 * @since 1.0.0
642 */
643 public function widget_textdomain() {
644
645 $domain = $this->plugin_slug;
646 $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
647
648 load_textdomain( $domain, WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . $locale . '.mo' );
649 load_plugin_textdomain( $domain, FALSE, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );
650
651 } // end widget_textdomain
652
653 /**
654 * Registers and enqueues admin-specific styles.
655 *
656 * @since 1.0.0
657 */
658 public function register_admin_styles() {
659
660 if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
661 return;
662 }
663
664 $screen = get_current_screen();
665 if ( $screen->id == $this->plugin_screen_hook_suffix ) {
666 wp_enqueue_style( $this->plugin_slug .'-admin-styles', plugins_url( 'style/admin.css', __FILE__ ), array(), $this->version );
667 }
668
669 } // end register_admin_styles
670
671 /**
672 * Registers and enqueues admin-specific JavaScript.
673 *
674 * @since 2.3.4
675 */
676 public function register_admin_scripts() {
677
678 if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
679 return;
680 }
681
682 $screen = get_current_screen();
683 if ( $screen->id == $this->plugin_screen_hook_suffix ) {
684 wp_enqueue_script( 'thickbox' );
685 wp_enqueue_style( 'thickbox' );
686 wp_enqueue_script( 'media-upload' );
687 wp_enqueue_script( $this->plugin_slug .'-admin-script', plugins_url( 'js/admin.js', __FILE__ ), array('jquery'), $this->version );
688 }
689
690 } // end register_admin_scripts
691
692 /**
693 * Hooks into getttext to change upload button text when uploader is called by WPP.
694 *
695 * @since 2.3.4
696 */
697 function thickbox_setup() {
698
699 global $pagenow;
700 if ( 'media-upload.php' == $pagenow || 'async-upload.php' == $pagenow ) {
701 add_filter( 'gettext', array( $this, 'replace_thickbox_text' ), 1, 3 );
702 }
703
704 } // end thickbox_setup
705
706 /**
707 * Replaces upload button text when uploader is called by WPP.
708 *
709 * @since 2.3.4
710 * @param string translated_text
711 * @param string text
712 * @param string domain
713 * @return string
714 */
715 function replace_thickbox_text($translated_text, $text, $domain) {
716
717 if ('Insert into Post' == $text) {
718 $referer = strpos( wp_get_referer(), 'wpp_admin' );
719 if ( $referer != '' ) {
720 return __('Upload', $this->plugin_slug );
721 }
722 }
723
724 return $translated_text;
725
726 } // end replace_thickbox_text
727
728 /**
729 * Registers and enqueues widget-specific styles.
730 *
731 * @since 1.0.0
732 */
733 public function register_widget_styles() {
734
735 $theme_file = get_stylesheet_directory() . '/wpp.css';
736 $plugin_file = plugin_dir_path(__FILE__) . 'style/wpp.css';
737
738 if ( @file_exists($theme_file) ) { // user stored a custom wpp.css on theme's directory, so use it
739 wp_enqueue_style( $this->plugin_slug, get_stylesheet_directory_uri() . "/wpp.css", array(), $this->version );
740 } elseif ( @file_exists($plugin_file) ) { // no custom wpp.css, use plugin's instead
741 wp_enqueue_style( $this->plugin_slug, plugins_url( 'style/wpp.css', __FILE__ ), array(), $this->version );
742 }
743
744 } // end register_widget_styles
745
746 /**
747 * Registers and enqueues widget-specific scripts.
748 */
749 public function register_widget_scripts() {
750 // We need jQuery in the front-end only when ajaxifying the widget
751 if ( $this->user_settings['tools']['ajax'] )
752 wp_enqueue_script( 'jquery' );
753 } // end register_widget_scripts
754
755 /**
756 * Register the administration menu for this plugin into the WordPress Dashboard menu.
757 *
758 * @since 1.0.0
759 */
760 public function add_plugin_admin_menu() {
761
762 $this->plugin_screen_hook_suffix = add_options_page(
763 'WordPress Popular Posts',
764 'WordPress Popular Posts',
765 'manage_options',
766 $this->plugin_slug,
767 array( $this, 'display_plugin_admin_page' )
768 );
769
770 }
771
772 /**
773 * Render the settings page for this plugin.
774 *
775 * @since 1.0.0
776 */
777 public function display_plugin_admin_page() {
778 include_once( 'views/admin.php' );
779 }
780
781 /**
782 * Registers Settings link on plugin description.
783 *
784 * @since 2.3.3
785 * @param array links
786 * @param string file
787 * @return array
788 */
789 public function add_plugin_settings_link( $links, $file ){
790
791 $this_plugin = plugin_basename(__FILE__);
792
793 if ( is_plugin_active($this_plugin) && $file == $this_plugin ) {
794 $links[] = '<a href="' . admin_url( 'options-general.php?page=wordpress-popular-posts' ) . '">Settings</a>';
795 }
796
797 return $links;
798
799 } // end add_plugin_settings_link
800
801 /*--------------------------------------------------*/
802 /* Install / activation / deactivation methods
803 /*--------------------------------------------------*/
804
805 /**
806 * Return an instance of this class.
807 *
808 * @since 3.0.0
809 * @return object A single instance of this class.
810 */
811 public static function get_instance() {
812
813 // If the single instance hasn't been set, set it now.
814 if ( NULL == self::$instance ) {
815 self::$instance = new self;
816 }
817
818 return self::$instance;
819
820 } // end get_instance
821
822 /**
823 * Fired when the plugin is activated.
824 *
825 * @since 1.0.0
826 * @global object wpdb
827 * @param bool network_wide True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog.
828 */
829 public static function activate( $network_wide ) {
830
831 global $wpdb;
832
833 if ( function_exists( 'is_multisite' ) && is_multisite() ) {
834
835 // run activation for each blog in the network
836 if ( $network_wide ) {
837
838 $original_blog_id = get_current_blog_id();
839 $blogs_ids = $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" );
840
841 foreach( $blogs_ids as $blog_id ) {
842 switch_to_blog( $blog_id );
843 self::__activate();
844 }
845
846 // switch back to current blog
847 switch_to_blog( $original_blog_id );
848
849 return;
850
851 }
852
853 }
854
855 self::__activate();
856
857 } // end activate
858
859 /**
860 * Fired when a new blog is activated on WP Multisite.
861 *
862 * @since 3.0.0
863 * @param int blog_id New blog ID
864 */
865 public function activate_new_site( $blog_id ){
866
867 if ( 1 !== did_action( 'wpmu_new_blog' ) )
868 return;
869
870 // run activation for the new blog
871 switch_to_blog( $blog_id );
872 self::__activate();
873
874 // switch back to current blog
875 restore_current_blog();
876
877 } // end activate_new_site
878
879 /**
880 * On plugin activation, checks that the WPP database tables are present.
881 *
882 * @since 2.4.0
883 * @global object wpdb
884 */
885 private static function __activate() {
886
887 global $wpdb;
888
889 // set table name
890 $prefix = $wpdb->prefix . "popularposts";
891
892 // fresh setup
893 if ( $prefix != $wpdb->get_var("SHOW TABLES LIKE '{$prefix}data'") ) {
894 self::__do_db_tables( $prefix );
895 }
896
897 } // end __activate
898
899 /**
900 * Fired when the plugin is deactivated.
901 *
902 * @since 1.0.0
903 * @global object wpbd
904 * @param bool network_wide True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog
905 */
906 public static function deactivate( $network_wide ) {
907
908 global $wpdb;
909
910 if ( function_exists( 'is_multisite' ) && is_multisite() ) {
911
912 // Run deactivation for each blog in the network
913 if ( $network_wide ) {
914
915 $original_blog_id = get_current_blog_id();
916 $blogs_ids = $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" );
917
918 foreach( $blogs_ids as $blog_id ) {
919 switch_to_blog( $blog_id );
920 self::__deactivate();
921 }
922
923 // Switch back to current blog
924 switch_to_blog( $original_blog_id );
925
926 return;
927
928 }
929
930 }
931
932 self::__deactivate();
933
934 } // end deactivate
935
936 /**
937 * On plugin deactivation, disables the shortcode and removes the scheduled task.
938 *
939 * @since 2.4.0
940 */
941 private static function __deactivate() {
942
943 remove_shortcode('wpp');
944 wp_clear_scheduled_hook('wpp_cache_event');
945
946 } // end __deactivate
947
948 /**
949 * Checks if an upgrade procedure is required.
950 *
951 * @since 2.4.0
952 */
953 public function upgrade_check(){
954
955 // Get WPP version
956 $wpp_ver = get_site_option('wpp_ver');
957
958 if ( !$wpp_ver ) {
959 add_site_option('wpp_ver', $this->version);
960 } elseif ( version_compare($wpp_ver, $this->version, '<') ) {
961 $this->__upgrade();
962 }
963
964 } // end upgrade_check
965
966 /**
967 * On plugin upgrade, performs a number of actions: update WPP database tables structures (if needed),
968 * run the setup wizard (if needed), and some other checks.
969 *
970 * @since 2.4.0
971 * @global object wpdb
972 */
973 private function __upgrade() {
974
975 global $wpdb;
976
977 // set table name
978 $prefix = $wpdb->prefix . "popularposts";
979
980 // validate the structure of the tables and create missing tables
981 self::__do_db_tables( $prefix );
982
983 // If summary is empty, import data from popularpostsdatacache
984 if ( !$wpdb->get_var("SELECT COUNT(*) FROM {$prefix}summary") ) {
985
986 // popularpostsdatacache table is still there
987 if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}datacache'") ) {
988
989 $sql = "
990 INSERT INTO {$prefix}summary (postid, pageviews, view_date, last_viewed)
991 SELECT id, pageviews, day_no_time, day
992 FROM {$prefix}datacache
993 GROUP BY day_no_time, id
994 ORDER BY day_no_time DESC";
995
996 $result = $wpdb->query( $sql );
997
998 // Rename old caching table
999 if ( $result ) {
1000 $result = $wpdb->query( "RENAME TABLE {$prefix}datacache TO {$prefix}datacache_backup;" );
1001 }
1002
1003 }
1004
1005 }
1006
1007 // Check indexes and fields
1008 $dataFields = $wpdb->get_results( "SHOW FIELDS FROM {$prefix}data;" );
1009
1010 // Update fields, if needed
1011 foreach ( $dataFields as $column ) {
1012 if ( "postid" == $column->Field && "bigint(20)" != $column->Type ) {
1013 $wpdb->query("ALTER TABLE {$prefix}data CHANGE postid postid bigint(20) NOT NULL;");
1014 }
1015
1016 if ( "pageviews" == $column->Field && "bigint(20)" != $column->Type ) {
1017 $wpdb->query("ALTER TABLE {$prefix}data CHANGE pageviews pageviews bigint(20) DEFAULT 1;");
1018 }
1019 }
1020
1021 // Update index, if needed
1022 $dataIndex = $wpdb->get_results("SHOW INDEX FROM {$prefix}data;", ARRAY_A);
1023
1024 if ( "PRIMARY" != $dataIndex[0]['Key_name'] ) {
1025 $wpdb->query("ALTER TABLE {$prefix}data DROP INDEX id, ADD PRIMARY KEY (postid);");
1026 }
1027
1028 // Update WPP version
1029 update_site_option('wpp_ver', $this->version);
1030
1031 } // end __upgrade
1032
1033 /**
1034 * Creates/updates the WPP database tables.
1035 *
1036 * @since 2.4.0
1037 * @global object wpdb
1038 */
1039 private static function __do_db_tables( $prefix ) {
1040
1041 global $wpdb;
1042
1043 $sql = "";
1044 $charset_collate = "";
1045
1046 if ( !empty($wpdb->charset) )
1047 $charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
1048
1049 if ( !empty($wpdb->collate) )
1050 $charset_collate .= "COLLATE {$wpdb->collate}";
1051
1052 $sql = "
1053 CREATE TABLE {$prefix}data (
1054 postid bigint(20) NOT NULL,
1055 day datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1056 last_viewed datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1057 pageviews bigint(20) DEFAULT 1,
1058 PRIMARY KEY (postid)
1059 ) {$charset_collate};
1060 CREATE TABLE {$prefix}summary (
1061 ID bigint(20) NOT NULL AUTO_INCREMENT,
1062 postid bigint(20) NOT NULL,
1063 pageviews bigint(20) NOT NULL DEFAULT 1,
1064 view_date date NOT NULL DEFAULT '0000-00-00',
1065 last_viewed datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1066 PRIMARY KEY (ID),
1067 UNIQUE KEY ID_date (postid,view_date),
1068 KEY postid (postid),
1069 KEY last_viewed (last_viewed)
1070 ) {$charset_collate};";
1071
1072 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1073 dbDelta($sql);
1074
1075 } // end __do_db_tables
1076
1077 /**
1078 * Checks if the technical requirements are met.
1079 *
1080 * @since 2.4.0
1081 * @link http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
1082 * @global string $wp_version
1083 * @return array
1084 */
1085 private function __check_requirements() {
1086
1087 global $wp_version;
1088
1089 $php_min_version = '5.2';
1090 $wp_min_version = '3.8';
1091 $php_current_version = phpversion();
1092 $errors = array();
1093
1094 if ( version_compare( $php_min_version, $php_current_version, '>' ) ) {
1095 $errors[] = sprintf(
1096 __( 'Your PHP installation is too old. WordPress Popular Posts requires at least PHP version %1$s to function correctly. Please contact your hosting provider and ask them to upgrade PHP to %1$s or higher.', $this->plugin_slug ),
1097 $php_min_version
1098 );
1099 }
1100
1101 if ( version_compare( $wp_min_version, $wp_version, '>' ) ) {
1102 $errors[] = sprintf(
1103 __( 'Your WordPress version is too old. WordPress Popular Posts requires at least WordPress version %1$s to function correctly. Please update your blog via Dashboard &gt; Update.', $this->plugin_slug ),
1104 $wp_min_version
1105 );
1106 }
1107
1108 return $errors;
1109
1110 } // end __check_requirements
1111
1112 /**
1113 * Outputs error messages to wp-admin.
1114 *
1115 * @since 2.4.0
1116 */
1117 public function check_admin_notices() {
1118
1119 $errors = $this->__check_requirements();
1120
1121 if ( empty($errors) )
1122 return;
1123
1124 if ( isset($_GET['activate']) )
1125 unset($_GET['activate']);
1126
1127 printf(
1128 __('<div class="error"><p>%1$s</p><p><i>%2$s</i> has been <strong>deactivated</strong>.</p></div>', $this->plugin_slug),
1129 join( '</p><p>', $errors ),
1130 'WordPress Popular Posts'
1131 );
1132
1133 deactivate_plugins( plugin_basename( __FILE__ ) );
1134
1135 } // end check_admin_notices
1136
1137
1138 /*--------------------------------------------------*/
1139 /* Plugin methods / functions
1140 /*--------------------------------------------------*/
1141
1142 /**
1143 * Purges deleted posts from data/summary tables.
1144 *
1145 * @since 2.0.0
1146 * @global object $wpdb
1147 */
1148 public function purge_data() {
1149
1150 global $wpdb;
1151
1152 if ( $missing = $wpdb->get_results( "SELECT v.postid AS id FROM {$wpdb->prefix}popularpostsdata v WHERE NOT EXISTS (SELECT p.ID FROM {$wpdb->posts} p WHERE v.postid = p.ID);" ) ) {
1153 $to_be_deleted = '';
1154
1155 foreach ( $missing as $deleted )
1156 $to_be_deleted .= $deleted->id . ",";
1157
1158 $to_be_deleted = rtrim( $to_be_deleted, "," );
1159
1160 $wpdb->query( "DELETE FROM {$wpdb->prefix}popularpostsdata WHERE postid IN({$to_be_deleted});" );
1161 $wpdb->query( "DELETE FROM {$wpdb->prefix}popularpostssummary WHERE postid IN({$to_be_deleted});" );
1162 }
1163
1164 } // end purge_data
1165
1166 /**
1167 * Truncates data and cache on demand.
1168 *
1169 * @since 2.0.0
1170 * @global object wpdb
1171 */
1172 public function clear_data() {
1173
1174 $token = $_POST['token'];
1175 $clear = isset($_POST['clear']) ? $_POST['clear'] : '';
1176 $key = get_site_option("wpp_rand");
1177
1178 if (current_user_can('manage_options') && ($token === $key) && !empty($clear)) {
1179 global $wpdb;
1180
1181 // set table name
1182 $prefix = $wpdb->prefix . "popularposts";
1183
1184 if ($clear == 'cache') {
1185 if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}summary'") ) {
1186 $wpdb->query("TRUNCATE TABLE {$prefix}summary;");
1187 $this->__flush_transients();
1188 _e('Success! The cache table has been cleared!', $this->plugin_slug);
1189 } else {
1190 _e('Error: cache table does not exist.', $this->plugin_slug);
1191 }
1192 } else if ($clear == 'all') {
1193 if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}data'") && $wpdb->get_var("SHOW TABLES LIKE '{$prefix}summary'") ) {
1194 $wpdb->query("TRUNCATE TABLE {$prefix}data;");
1195 $wpdb->query("TRUNCATE TABLE {$prefix}summary;");
1196 $this->__flush_transients();
1197 _e('Success! All data have been cleared!', $this->plugin_slug);
1198 } else {
1199 _e('Error: one or both data tables are missing.', $this->plugin_slug);
1200 }
1201 } else {
1202 _e('Invalid action.', $this->plugin_slug);
1203 }
1204 } else {
1205 _e('Sorry, you do not have enough permissions to do this. Please contact the site administrator for support.', $this->plugin_slug);
1206 }
1207
1208 die();
1209
1210 } // end clear_data
1211
1212 /**
1213 * Truncates thumbnails cache on demand.
1214 *
1215 * @since 2.0.0
1216 * @global object wpdb
1217 */
1218 public function clear_thumbnails() {
1219
1220 $token = $_POST['token'];
1221 $key = get_site_option("wpp_rand");
1222
1223 if ( current_user_can('manage_options') && ($token === $key) ) {
1224 $wp_upload_dir = wp_upload_dir();
1225
1226 if ( is_dir( $wp_upload_dir['basedir'] . "/" . $this->plugin_slug ) ) {
1227 $files = glob( $wp_upload_dir['basedir'] . "/" . $this->plugin_slug . "/*" ); // get all file names
1228
1229 if ( is_array($files) && !empty($files) ) {
1230 foreach($files as $file){ // iterate files
1231 if ( is_file($file) )
1232 @unlink($file); // delete file
1233 }
1234
1235 _e('Success! All files have been deleted!', $this->plugin_slug);
1236 } else {
1237 _e('The thumbnail cache is already empty!', $this->plugin_slug);
1238 }
1239 } else {
1240 _e('Invalid action.', $this->plugin_slug);
1241 }
1242 } else {
1243 _e('Sorry, you do not have enough permissions to do this. Please contact the site administrator for support.', $this->plugin_slug);
1244 }
1245
1246 die();
1247
1248 } // end clear_data
1249
1250 /**
1251 * Updates views count on page load via AJAX.
1252 *
1253 * @since 2.0.0
1254 */
1255 public function update_views_ajax(){
1256
1257 if ( !wp_verify_nonce($_POST['token'], 'wpp-token') || !$this->__is_numeric($_POST['id']) )
1258 die("WPP: Oops, invalid request!");
1259
1260 $nonce = $_POST['token'];
1261 $post_ID = $_POST['id'];
1262
1263 $exec_time = 0;
1264
1265 $start = $this->__microtime_float();
1266 $result = $this->__update_views($post_ID);
1267 $end = $this->__microtime_float();
1268
1269 $exec_time += round($end - $start, 6);
1270
1271 if ( $result ) {
1272 die( "WPP: OK. Execution time: " . $exec_time . " seconds" );
1273 }
1274
1275 die( "WPP: Oops, could not update the views count!" );
1276
1277 } // end update_views_ajax
1278
1279 /**
1280 * Outputs script to update views via AJAX.
1281 *
1282 * @since 2.0.0
1283 * @global object post
1284 */
1285 public function print_ajax(){
1286
1287 if ( $this->current_post_id ) {
1288 ?>
1289 <!-- WordPress Popular Posts v<?php echo $this->version; ?> -->
1290 <script type="text/javascript">//<![CDATA[
1291
1292 // Create XMLHttpRequest object and set variables
1293 var xhr = ( window.XMLHttpRequest )
1294 ? new XMLHttpRequest()
1295 : new ActiveXObject( "Microsoft.XMLHTTP" ),
1296 url = '<?php echo admin_url('admin-ajax.php'); ?>',
1297 params = 'action=update_views_ajax&token=<?php echo wp_create_nonce('wpp-token') ?>&id=<?php echo $this->current_post_id; ?>';
1298 // Set request method and target URL
1299 xhr.open( "POST", url, true );
1300 // Set request header
1301 xhr.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
1302 // Hook into onreadystatechange
1303 xhr.onreadystatechange = function() {
1304 if ( 4 == xhr.readyState && 200 == xhr.status ) {
1305 if ( window.console && window.console.log ) {
1306 window.console.log( xhr.responseText );
1307 }
1308 }
1309 }
1310 // Send request
1311 xhr.send( params );
1312
1313 //]]></script>
1314 <!-- End WordPress Popular Posts v<?php echo $this->version; ?> -->
1315 <?php
1316 }
1317
1318 } // end print_ajax
1319
1320 /**
1321 * Deletes cached (transient) data.
1322 *
1323 * @since 3.0.0
1324 */
1325 private function __flush_transients() {
1326
1327 $wpp_transients = get_site_option('wpp_transients');
1328
1329 if ( $wpp_transients && is_array($wpp_transients) && !empty($wpp_transients) ) {
1330 for ($t=0; $t < count($wpp_transients); $t++)
1331 delete_transient( $wpp_transients[$t] );
1332
1333 update_site_option('wpp_transients', array());
1334 }
1335
1336 } // end __flush_transients
1337
1338 /**
1339 * Updates views count.
1340 *
1341 * @since 1.4.0
1342 * @global object $wpdb
1343 * @param int Post ID
1344 * @return bool|int FALSE if query failed, TRUE on success
1345 */
1346 private function __update_views($id) {
1347
1348 /*
1349 TODO:
1350 For WordPress Multisite, we must define the DIEONDBERROR constant for database errors to display like so:
1351 <?php define( 'DIEONDBERROR', true ); ?>
1352 */
1353
1354 global $wpdb;
1355 $table = $wpdb->prefix . "popularposts";
1356 $wpdb->show_errors();
1357
1358 // WPML support, get original post/page ID
1359 if ( defined('ICL_LANGUAGE_CODE') && function_exists('icl_object_id') ) {
1360 global $sitepress;
1361 $id = icl_object_id( $id, get_post_type( $id ), true, $sitepress->get_default_language() );
1362 }
1363
1364 $now = $this->__now();
1365 $curdate = $this->__curdate();
1366 $views = ( $this->user_settings['tools']['sampling']['active'] )
1367 ? $this->user_settings['tools']['sampling']['rate']
1368 : 1;
1369
1370 // Update all-time table
1371 $result1 = $wpdb->query( $wpdb->prepare(
1372 "INSERT INTO {$table}data
1373 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
1374 ON DUPLICATE KEY UPDATE pageviews = pageviews + %4\$d, last_viewed = '%3\$s';",
1375 $id,
1376 $now,
1377 $now,
1378 $views
1379 ));
1380
1381 // Update range (summary) table
1382 $result2 = $wpdb->query( $wpdb->prepare(
1383 "INSERT INTO {$table}summary
1384 (postid, pageviews, view_date, last_viewed) VALUES (%d, %d, %s, %s)
1385 ON DUPLICATE KEY UPDATE pageviews = pageviews + %2\$d, last_viewed = '%4\$s';",
1386 $id,
1387 $views,
1388 $curdate,
1389 $now
1390 ));
1391
1392 if ( !$result1 || !$result2 )
1393 return false;
1394
1395 // Allow WP themers / coders perform an action
1396 // after updating views count
1397 if ( has_action( 'wpp_update_views' ) )
1398 do_action( 'wpp_update_views', $id );
1399
1400 return true;
1401
1402 } // end __update_views
1403
1404 /**
1405 * Queries the database and returns the posts (if any met the criteria set by the user).
1406 *
1407 * @since 1.4.0
1408 * @global object $wpdb
1409 * @param array Widget instance
1410 * @return null|array Array of posts, or null if nothing was found
1411 */
1412 protected function _query_posts($instance) {
1413
1414 global $wpdb;
1415
1416 // parse instance values
1417 $instance = $this->__merge_array_r(
1418 $this->defaults,
1419 $instance
1420 );
1421
1422 $prefix = $wpdb->prefix . "popularposts";
1423 $fields = "p.ID AS 'id', p.post_title AS 'title', p.post_date AS 'date', p.post_author AS 'uid'";
1424 $from = "";
1425 $where = "WHERE 1 = 1";
1426 $orderby = "";
1427 $groupby = "";
1428 $limit = "LIMIT {$instance['limit']}";
1429
1430 $post_types = "";
1431 $pids = "";
1432 $cats = "";
1433 $authors = "";
1434 $content = "";
1435
1436 $now = $this->__now();
1437
1438 // post filters
1439 // * freshness - get posts published within the selected time range only
1440 if ( $instance['freshness'] ) {
1441 switch( $instance['range'] ){
1442 case "daily":
1443 $where .= " AND p.post_date > DATE_SUB('{$now}', INTERVAL 1 DAY) ";
1444 break;
1445
1446 case "weekly":
1447 $where .= " AND p.post_date > DATE_SUB('{$now}', INTERVAL 1 WEEK) ";
1448 break;
1449
1450 case "monthly":
1451 $where .= " AND p.post_date > DATE_SUB('{$now}', INTERVAL 1 MONTH) ";
1452 break;
1453
1454 default:
1455 $where .= "";
1456 break;
1457 }
1458 }
1459
1460 // * post types - based on code seen at https://github.com/williamsba/WordPress-Popular-Posts-with-Custom-Post-Type-Support
1461 $types = explode(",", $instance['post_type']);
1462 $sql_post_types = "";
1463 $join_cats = true;
1464
1465 // if we're getting just pages, why join the categories table?
1466 if ( 'page' == strtolower($instance['post_type']) ) {
1467
1468 $join_cats = false;
1469 $where .= " AND p.post_type = '{$instance['post_type']}'";
1470
1471 }
1472 // we're listing other custom type(s)
1473 else {
1474
1475 if ( count($types) > 1 ) {
1476
1477 foreach ( $types as $post_type ) {
1478 $sql_post_types .= "'{$post_type}',";
1479 }
1480
1481 $sql_post_types = rtrim( $sql_post_types, ",");
1482 $where .= " AND p.post_type IN({$sql_post_types})";
1483
1484 } else {
1485 $where .= " AND p.post_type = '{$instance['post_type']}'";
1486 }
1487
1488 }
1489
1490 // * posts exclusion
1491 if ( !empty($instance['pid']) ) {
1492
1493 $ath = explode(",", $instance['pid']);
1494
1495 $where .= ( count($ath) > 1 )
1496 ? " AND p.ID NOT IN({$instance['pid']})"
1497 : " AND p.ID <> '{$instance['pid']}'";
1498
1499 }
1500
1501 // * categories
1502 if ( !empty($instance['cat']) && $join_cats ) {
1503
1504 $cat_ids = explode(",", $instance['cat']);
1505 $in = array();
1506 $out = array();
1507 $not_in = "";
1508
1509 usort($cat_ids, array(&$this, '__sorter'));
1510
1511 for ($i=0; $i < count($cat_ids); $i++) {
1512 if ($cat_ids[$i] >= 0) $in[] = $cat_ids[$i];
1513 if ($cat_ids[$i] < 0) $out[] = $cat_ids[$i];
1514 }
1515
1516 $in_cats = implode(",", $in);
1517 $out_cats = implode(",", $out);
1518 $out_cats = preg_replace( '|[^0-9,]|', '', $out_cats );
1519
1520 if ($in_cats != "" && $out_cats == "") { // get posts from from given cats only
1521 $where .= " AND p.ID IN (
1522 SELECT object_id
1523 FROM {$wpdb->term_relationships} AS r
1524 JOIN {$wpdb->term_taxonomy} AS x ON x.term_taxonomy_id = r.term_taxonomy_id
1525 JOIN {$wpdb->terms} AS t ON t.term_id = x.term_id
1526 WHERE x.taxonomy = 'category' AND t.term_id IN({$in_cats})
1527 )";
1528 } else if ($in_cats == "" && $out_cats != "") { // exclude posts from given cats only
1529 $where .= " AND p.ID NOT IN (
1530 SELECT object_id
1531 FROM {$wpdb->term_relationships} AS r
1532 JOIN {$wpdb->term_taxonomy} AS x ON x.term_taxonomy_id = r.term_taxonomy_id
1533 JOIN {$wpdb->terms} AS t ON t.term_id = x.term_id
1534 WHERE x.taxonomy = 'category' AND t.term_id IN({$out_cats})
1535 )";
1536 } else { // mixed, and possibly a heavy load on the DB
1537 $where .= " AND p.ID IN (
1538 SELECT object_id
1539 FROM {$wpdb->term_relationships} AS r
1540 JOIN {$wpdb->term_taxonomy} AS x ON x.term_taxonomy_id = r.term_taxonomy_id
1541 JOIN {$wpdb->terms} AS t ON t.term_id = x.term_id
1542 WHERE x.taxonomy = 'category' AND t.term_id IN({$in_cats})
1543 ) AND p.ID NOT IN (
1544 SELECT object_id
1545 FROM {$wpdb->term_relationships} AS r
1546 JOIN {$wpdb->term_taxonomy} AS x ON x.term_taxonomy_id = r.term_taxonomy_id
1547 JOIN {$wpdb->terms} AS t ON t.term_id = x.term_id
1548 WHERE x.taxonomy = 'category' AND t.term_id IN({$out_cats})
1549 )";
1550 }
1551
1552 }
1553
1554 // * authors
1555 if ( !empty($instance['author']) ) {
1556
1557 $ath = explode(",", $instance['author']);
1558
1559 $where .= ( count($ath) > 1 )
1560 ? " AND p.post_author IN({$instance['author']})"
1561 : " AND p.post_author = '{$instance['author']}'";
1562
1563 }
1564
1565 // All-time range
1566 if ( "all" == $instance['range'] ) {
1567
1568 $fields .= ", p.comment_count AS 'comment_count'";
1569
1570 // order by comments
1571 if ( "comments" == $instance['order_by'] ) {
1572
1573 $from = "{$wpdb->posts} p";
1574 $where .= " AND p.comment_count > 0 AND p.post_password = '' AND p.post_status = 'publish'";
1575 $orderby = " ORDER BY p.comment_count DESC";
1576
1577 // get views, too
1578 if ( $instance['stats_tag']['views'] ) {
1579
1580 $fields .= ", IFNULL(v.pageviews, 0) AS 'pageviews'";
1581 $from .= " LEFT JOIN {$prefix}data v ON p.ID = v.postid";
1582
1583 }
1584
1585 }
1586 // order by (avg) views
1587 else {
1588
1589 $from = "{$prefix}data v LEFT JOIN {$wpdb->posts} p ON v.postid = p.ID";
1590 $where .= " AND p.post_password = '' AND p.post_status = 'publish'";
1591
1592 // order by views
1593 if ( "views" == $instance['order_by'] ) {
1594
1595 $fields .= ", v.pageviews AS 'pageviews'";
1596 $orderby = "ORDER BY pageviews DESC";
1597
1598 }
1599 // order by avg views
1600 elseif ( "avg" == $instance['order_by'] ) {
1601
1602 $fields .= ", ( v.pageviews/(IF ( DATEDIFF('{$now}', MIN(v.day)) > 0, DATEDIFF('{$now}', MIN(v.day)), 1) ) ) AS 'avg_views'";
1603 $groupby = "GROUP BY v.postid";
1604 $orderby = "ORDER BY avg_views DESC";
1605
1606 }
1607
1608 }
1609
1610 $query = "SELECT {$fields} FROM {$from} {$where} {$groupby} {$orderby} {$limit};";
1611
1612 } else { // CUSTOM RANGE
1613
1614 $interval = "";
1615
1616 switch( $instance['range'] ){
1617 case "daily":
1618 $interval = "1 DAY";
1619 break;
1620
1621 case "weekly":
1622 $interval = "1 WEEK";
1623 break;
1624
1625 case "monthly":
1626 $interval = "1 MONTH";
1627 break;
1628
1629 default:
1630 $interval = "1 DAY";
1631 break;
1632 }
1633
1634 // order by comments
1635 if ( "comments" == $instance['order_by'] ) {
1636
1637 $fields .= ", c.comment_count AS 'comment_count'";
1638 $from = "(SELECT comment_post_ID AS 'id', COUNT(comment_post_ID) AS 'comment_count' FROM {$wpdb->comments} WHERE comment_date_gmt > DATE_SUB('{$now}', INTERVAL {$interval}) AND comment_approved = 1 GROUP BY id ORDER BY comment_count DESC) c LEFT JOIN {$wpdb->posts} p ON c.id = p.ID";
1639 $where .= " AND p.post_password = '' AND p.post_status = 'publish'";
1640
1641 if ( $instance['stats_tag']['views'] ) { // get views, too
1642
1643 $fields .= ", IFNULL(v.pageviews, 0) AS 'pageviews'";
1644 $from .= " LEFT JOIN (SELECT postid, SUM(pageviews) AS pageviews FROM {$prefix}summary WHERE last_viewed > DATE_SUB('{$now}', INTERVAL {$interval}) GROUP BY postid) v ON p.ID = v.postid";
1645
1646 }
1647
1648 }
1649 // ordered by views / avg
1650 else {
1651
1652 $from = "(SELECT postid, IFNULL(SUM(pageviews), 0) AS pageviews FROM {$prefix}summary WHERE last_viewed > DATE_SUB('{$now}', INTERVAL {$interval}) GROUP BY postid ORDER BY pageviews DESC) v LEFT JOIN {$wpdb->posts} p ON v.postid = p.ID";
1653 $where .= " AND p.post_password = '' AND p.post_status = 'publish'";
1654
1655 // ordered by views
1656 if ( "views" == $instance['order_by'] ) {
1657 $fields .= ", v.pageviews AS 'pageviews'";
1658 }
1659 // ordered by avg views
1660 elseif ( "avg" == $instance['order_by'] ) {
1661
1662 $fields .= ", ( v.pageviews/(IF ( DATEDIFF('{$now}', DATE_SUB('{$now}', INTERVAL {$interval})) > 0, DATEDIFF('{$now}', DATE_SUB('{$now}', INTERVAL {$interval})), 1) ) ) AS 'avg_views' ";
1663 $groupby = "GROUP BY v.postid";
1664 $orderby = "ORDER BY avg_views DESC";
1665
1666 }
1667
1668 // get comments, too
1669 if ( $instance['stats_tag']['comment_count'] ) {
1670
1671 $fields .= ", IFNULL(c.comment_count, 0) AS 'comment_count'";
1672 $from .= " LEFT JOIN (SELECT comment_post_ID AS 'id', COUNT(comment_post_ID) AS 'comment_count' FROM {$wpdb->comments} WHERE comment_date_gmt > DATE_SUB('{$now}', INTERVAL {$interval}) AND comment_approved = 1 GROUP BY id) c ON p.ID = c.id";
1673
1674 }
1675
1676 }
1677
1678 $query = "SELECT {$fields} FROM {$from} {$where} {$groupby} {$orderby} {$limit};";
1679
1680 }
1681
1682 $this->__debug( $query );
1683
1684 $result = $wpdb->get_results($query);
1685
1686 return apply_filters( 'wpp_query_posts', $result, $instance );
1687
1688 } // end query_posts
1689
1690 /**
1691 * Returns the formatted list of posts.
1692 *
1693 * @since 3.0.0
1694 * @param array instance The current instance of the widget / shortcode parameters
1695 * @return string HTML list of popular posts
1696 */
1697 private function __get_popular_posts( $instance ) {
1698
1699 // Parse instance values
1700 $instance = $this->__merge_array_r(
1701 $this->defaults,
1702 $instance
1703 );
1704
1705 $content = "";
1706
1707 // Fetch posts
1708 if ( !defined('WPP_ADMIN') && $this->user_settings['tools']['cache']['active'] ) {
1709 $transient_name = md5(json_encode($instance));
1710 $mostpopular = ( function_exists( 'is_multisite' ) && is_multisite() )
1711 ? get_site_transient( $transient_name )
1712 : get_transient( $transient_name );
1713
1714 $content = "\n" . "<!-- cached -->" . "\n";
1715
1716 // It wasn't there, so regenerate the data and save the transient
1717 if ( false === $mostpopular ) {
1718 $mostpopular = $this->_query_posts( $instance );
1719
1720 switch($this->user_settings['tools']['cache']['interval']['time']){
1721 case 'minute':
1722 $time = 60;
1723 break;
1724
1725 case 'hour':
1726 $time = 60 * 60;
1727 break;
1728
1729 case 'day':
1730 $time = 60 * 60 * 24;
1731 break;
1732
1733 case 'week':
1734 $time = 60 * 60 * 24 * 7;
1735 break;
1736
1737 case 'month':
1738 $time = 60 * 60 * 24 * 30;
1739 break;
1740
1741 case 'year':
1742 $time = 60 * 60 * 24 * 365;
1743 break;
1744 }
1745
1746 $expiration = $time * $this->user_settings['tools']['cache']['interval']['value'];
1747
1748 if ( function_exists( 'is_multisite' ) && is_multisite() )
1749 set_site_transient( $transient_name, $mostpopular, $expiration );
1750 else
1751 set_transient( $transient_name, $mostpopular, $expiration );
1752
1753 $wpp_transients = get_site_option('wpp_transients');
1754
1755 if ( !$wpp_transients ) {
1756 $wpp_transients = array( $transient_name );
1757 add_site_option('wpp_transients', $wpp_transients);
1758 } else {
1759 if ( !in_array($transient_name, $wpp_transients) ) {
1760 $wpp_transients[] = $transient_name;
1761 update_site_option('wpp_transients', $wpp_transients);
1762 }
1763 }
1764 }
1765 } else {
1766 $mostpopular = $this->_query_posts( $instance );
1767 }
1768
1769 // No posts to show
1770 if ( !is_array($mostpopular) || empty($mostpopular) ) {
1771 return "<p>".__('Sorry. No data so far.', $this->plugin_slug)."</p>";
1772 }
1773
1774 // Allow WP themers / coders access to raw data
1775 // so they can build their own output
1776 if ( has_filter( 'wpp_custom_html' ) && !defined('WPP_ADMIN') ) {
1777 return apply_filters( 'wpp_custom_html', $mostpopular, $instance );
1778 }
1779
1780 // HTML wrapper
1781 if ($instance['markup']['custom_html']) {
1782 $content .= "\n" . htmlspecialchars_decode($instance['markup']['wpp-start'], ENT_QUOTES) ."\n";
1783 } else {
1784 $content .= "\n" . "<ul class=\"wpp-list\">" . "\n";
1785 }
1786
1787 // Loop through posts
1788 foreach($mostpopular as $p) {
1789 $content .= $this->__render_popular_post( $p, $instance );
1790 }
1791
1792 // END HTML wrapper
1793 if ($instance['markup']['custom_html']) {
1794 $content .= "\n". htmlspecialchars_decode($instance['markup']['wpp-end'], ENT_QUOTES) ."\n";
1795 } else {
1796 $content .= "\n". "</ul>". "\n";
1797 }
1798
1799 return $content;
1800
1801 } // end __get_popular_posts
1802
1803 /**
1804 * Returns the formatted post.
1805 *
1806 * @since 3.0.0
1807 * @param object p
1808 * @param array instance The current instance of the widget / shortcode parameters
1809 * @return string
1810 */
1811 private function __render_popular_post($p, $instance) {
1812
1813 // WPML support, based on Serhat Evren's suggestion - see http://wordpress.org/support/topic/wpml-trick#post-5452607
1814 if ( defined('ICL_LANGUAGE_CODE') && function_exists('icl_object_id') ) {
1815 $current_id = icl_object_id( $p->id, get_post_type( $p->id ), true, ICL_LANGUAGE_CODE );
1816 $permalink = get_permalink( $current_id );
1817 } // Get original permalink
1818 else {
1819 $permalink = get_permalink($p->id);
1820 }
1821
1822 $title = $this->_get_title($p, $instance);
1823 $title_sub = $this->_get_title_sub($p, $instance);
1824
1825 $author = $this->_get_author($p, $instance);
1826 $post_cat = $this->_get_post_cat($p, $instance);
1827
1828 $thumb = $this->_get_thumb($p, $instance);
1829 $excerpt = $this->_get_excerpt($p, $instance);
1830
1831 $pageviews = $this->_get_pageviews($p, $instance);
1832 $comments = $this->_get_comments($p, $instance);
1833 $rating = $this->_get_rating($p, $instance);
1834 $date = $this->_get_date($p, $instance);
1835
1836 $_stats = join(' | ', $this->_get_stats($p, $instance));
1837
1838 // PUTTING IT ALL TOGETHER
1839 // build custom layout
1840 if ($instance['markup']['custom_html']) {
1841
1842 $data = array(
1843 'title' => '<a href="'.$permalink.'" title="'. esc_attr($title) .'" class="wpp-post-title" target="' . $this->user_settings['tools']['link']['target'] . '">'.$title_sub.'</a>',
1844 'summary' => $excerpt,
1845 'stats' => $_stats,
1846 'img' => '<a href="'.$permalink.'" title="'. esc_attr($title) .'" target="' . $this->user_settings['tools']['link']['target'] . '">' . $thumb . '</a>',
1847 'img_no_link' => $thumb,
1848 'id' => $p->id,
1849 'url' => $permalink,
1850 'text_title' => esc_attr($title),
1851 'category' => $post_cat,
1852 'author' => '<a href="' . get_author_posts_url($p->uid) . '">' . $author . '</a>',
1853 'views' => $pageviews,
1854 'comments' => $comments,
1855 'date' => $date
1856 );
1857
1858 $content = $this->__format_content( htmlspecialchars_decode($instance['markup']['post-html'], ENT_QUOTES ), $data, $instance['rating'] ). "\n";
1859
1860 }
1861 // build regular layout
1862 else {
1863 $content =
1864 '<li>'
1865 . '<a ' . ( ( $this->current_post_id == $p->id ) ? '' : 'href="' . $permalink . '"' ) . ' title="' . esc_attr($title) . '" target="' . $this->user_settings['tools']['link']['target'] . '">' . $thumb . '</a> '
1866 . '<a ' . ( ( $this->current_post_id == $p->id ) ? '' : 'href="' . $permalink . '"' ) . ' title="' . esc_attr($title) . '" class="wpp-post-title" target="' . $this->user_settings['tools']['link']['target'] . '">' . $title_sub . '</a> '
1867 . $excerpt . ' <span class="post-stats">' . $_stats . '</span> '
1868 . $rating
1869 . "</li>\n";
1870 }
1871
1872 return apply_filters('wpp_post', $content, $p, $instance);
1873
1874 } // end __render_popular_post
1875
1876 /**
1877 * Cache.
1878 *
1879 * @since 3.0.0
1880 * @param string $func function name
1881 * @param mixed $default
1882 * @return mixed
1883 */
1884 private function &__cache($func, $default = null) {
1885
1886 static $cache;
1887
1888 if ( !isset($cache) ) {
1889 $cache = array();
1890 }
1891
1892 if ( !isset($cache[$func]) ) {
1893 $cache[$func] = $default;
1894 }
1895
1896 return $cache[$func];
1897
1898 } // end __cache
1899
1900 /**
1901 * Gets post title.
1902 *
1903 * @since 3.0.0
1904 * @param object p
1905 * @param array instance The current instance of the widget / shortcode parameters
1906 * @return string
1907 */
1908 protected function _get_title($p, $instance) {
1909
1910 $cache = &$this->__cache(__FUNCTION__, array());
1911
1912 if ( isset($cache[$p->id]) ) {
1913 return $cache[$p->id];
1914 }
1915
1916 // WPML support, based on Serhat Evren's suggestion - see http://wordpress.org/support/topic/wpml-trick#post-5452607
1917 if ( defined('ICL_LANGUAGE_CODE') && function_exists('icl_object_id') ) {
1918 $current_id = icl_object_id( $p->id, get_post_type( $p->id ), true, ICL_LANGUAGE_CODE );
1919 $title = get_the_title( $current_id );
1920 } // Check for qTranslate
1921 else if ( $this->qTrans && function_exists('qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage') ) {
1922 $title = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $p->title );
1923 } // Use ol' plain title
1924 else {
1925 $title = $p->title;
1926 }
1927
1928 // Strip HTML tags
1929 $title = strip_tags($title);
1930
1931 return $cache[$p->id] = apply_filters('the_title', $title, $p->id);
1932
1933 } // end _get_title
1934
1935 /**
1936 * Gets substring of post title.
1937 *
1938 * @since 3.0.0
1939 * @param object p
1940 * @param array instance The current instance of the widget / shortcode parameters
1941 * @return string
1942 */
1943 protected function _get_title_sub($p, $instance) {
1944
1945 $cache = &$this->__cache(__FUNCTION__, array());
1946
1947 if ( isset($cache[$p->id]) ) {
1948 return $cache[$p->id];
1949 }
1950
1951 // TITLE
1952 $title_sub = $this->_get_title($p, $instance);
1953
1954 // truncate title
1955 if ($instance['shorten_title']['active']) {
1956 // by words
1957 if (isset($instance['shorten_title']['words']) && $instance['shorten_title']['words']) {
1958
1959 $words = explode(" ", $title_sub, $instance['shorten_title']['length'] + 1);
1960 if (count($words) > $instance['shorten_title']['length']) {
1961 array_pop($words);
1962 $title_sub = implode(" ", $words) . "...";
1963 }
1964
1965 }
1966 elseif (strlen($title_sub) > $instance['shorten_title']['length']) {
1967 $title_sub = mb_substr($title_sub, 0, $instance['shorten_title']['length'], $this->charset) . "...";
1968 }
1969 }
1970
1971 return $cache[$p->id] = $title_sub;
1972
1973 } // end _get_title_sub
1974
1975 /**
1976 * Gets post's excerpt.
1977 *
1978 * @since 3.0.0
1979 * @param object p
1980 * @param array instance The current instance of the widget / shortcode parameters
1981 * @return string
1982 */
1983 protected function _get_excerpt($p, $instance) {
1984
1985 $excerpt = '';
1986
1987 // EXCERPT
1988 if ($instance['post-excerpt']['active']) {
1989
1990 $excerpt = trim($this->_get_summary($p->id, $instance));
1991
1992 if (!empty($excerpt) && !$instance['markup']['custom_html']) {
1993 $excerpt = '<span class="wpp-excerpt">' . $excerpt . '</span>';
1994 }
1995
1996 }
1997
1998 return $excerpt;
1999
2000 } // end _get_excerpt
2001
2002 /**
2003 * Gets post's thumbnail.
2004 *
2005 * @since 3.0.0
2006 * @param object p
2007 * @param array instance The current instance of the widget / shortcode parameters
2008 * @return string
2009 */
2010 protected function _get_thumb($p, $instance) {
2011
2012 if ( !$instance['thumbnail']['active'] || !$this->thumbnailing ) {
2013 return '';
2014 }
2015
2016 $tbWidth = $instance['thumbnail']['width'];
2017 $tbHeight = $instance['thumbnail']['height'];
2018 $crop = $instance['thumbnail']['crop'];
2019 $permalink = get_permalink($p->id);
2020 $title = $this->_get_title($p, $instance);
2021
2022 $thumb = '';
2023
2024 // get image from custom field
2025 if ($this->user_settings['tools']['thumbnail']['source'] == "custom_field") {
2026 $path = get_post_meta($p->id, $this->user_settings['tools']['thumbnail']['field'], true);
2027
2028 if ($path != '') {
2029 // user has requested to resize cf image
2030 if ( $this->user_settings['tools']['thumbnail']['resize'] ) {
2031 $thumb .= $this->__get_img($p, null, $path, array($tbWidth, $tbHeight), $crop, $this->user_settings['tools']['thumbnail']['source'], $title);
2032 }
2033 // use original size
2034 else {
2035 $thumb .= $this->_render_image($path, array($tbWidth, $tbHeight), 'wpp-thumbnail wpp_cf', $title);
2036 }
2037 }
2038 else {
2039 $thumb .= $this->_render_image($this->default_thumbnail, array($tbWidth, $tbHeight), 'wpp-thumbnail wpp_cf_def', $title);
2040 }
2041 }
2042 // get image from post / Featured Image
2043 else {
2044 $thumb .= $this->__get_img($p, $p->id, null, array($tbWidth, $tbHeight), $crop, $this->user_settings['tools']['thumbnail']['source'], $title);
2045 }
2046
2047 return $cache[$p->id] = $thumb;
2048
2049 } // end _get_thumb
2050
2051 /**
2052 * Gets post's views.
2053 *
2054 * @since 3.0.0
2055 * @param object p
2056 * @param array instance The current instance of the widget / shortcode parameters
2057 * @return int|float
2058 */
2059 protected function _get_pageviews($p, $instance) {
2060
2061 $pageviews = 0;
2062
2063 if (
2064 $instance['order_by'] == "views"
2065 || $instance['order_by'] == "avg"
2066 || $instance['stats_tag']['views']
2067 ) {
2068 $pageviews = ($instance['order_by'] == "views" || $instance['order_by'] == "comments")
2069 ? $p->pageviews
2070 : $p->avg_views;
2071 }
2072
2073 return $pageviews;
2074
2075 } // end _get_pageviews
2076
2077 /**
2078 * Gets post's comment count.
2079 *
2080 * @since 3.0.0
2081 * @param object p
2082 * @param array instance The current instance of the widget / shortcode parameters
2083 * @return int
2084 */
2085 protected function _get_comments($p, $instance) {
2086
2087 $comments = ($instance['order_by'] == "comments" || $instance['stats_tag']['comment_count'])
2088 ? $p->comment_count
2089 : 0;
2090
2091 return $comments;
2092
2093 } // end _get_comments
2094
2095 /**
2096 * Gets post's rating.
2097 *
2098 * @since 3.0.0
2099 * @param object p
2100 * @param array instance The current instance of the widget / shortcode parameters
2101 * @return string
2102 */
2103 protected function _get_rating($p, $instance) {
2104
2105 $cache = &$this->__cache(__FUNCTION__, array());
2106
2107 if ( isset($cache[$p->id]) ) {
2108 return $cache[$p->id];
2109 }
2110
2111 $rating = '';
2112
2113 // RATING
2114 if (function_exists('the_ratings') && $instance['rating']) {
2115 $rating = '<span class="wpp-rating">' . the_ratings('span', $p->id, false) . '</span>';
2116 }
2117
2118 return $cache[$p->id] = $rating;
2119 } // end _get_rating
2120
2121 /**
2122 * Gets post's author.
2123 *
2124 * @since 3.0.0
2125 * @param object p
2126 * @param array instance The current instance of the widget / shortcode parameters
2127 * @return string
2128 */
2129 protected function _get_author($p, $instance) {
2130
2131 $cache = &$this->__cache(__FUNCTION__, array());
2132
2133 if ( isset($cache[$p->id]) ) {
2134 return $cache[$p->id];
2135 }
2136
2137 $author = ($instance['stats_tag']['author'])
2138 ? get_the_author_meta('display_name', $p->uid)
2139 : "";
2140
2141 return $cache[$p->id] = $author;
2142
2143 } // end _get_author
2144
2145 /**
2146 * Gets post's date.
2147 *
2148 * @since 3.0.0
2149 * @param object p
2150 * @param array instance The current instance of the widget / shortcode parameters
2151 * @return string
2152 */
2153 protected function _get_date($p, $instance) {
2154
2155 $cache = &$this->__cache(__FUNCTION__, array());
2156
2157 if ( isset($cache[$p->id]) ) {
2158 return $cache[$p->id];
2159 }
2160
2161 $date = date_i18n($instance['stats_tag']['date']['format'], strtotime($p->date));
2162 return $cache[$p->id] = $date;
2163
2164 } // end _get_date
2165
2166 /**
2167 * Gets post's category.
2168 *
2169 * @since 3.0.0
2170 * @param object p
2171 * @param array instance The current instance of the widget / shortcode parameters
2172 * @return string
2173 */
2174 protected function _get_post_cat($p, $instance) {
2175
2176 $post_cat = '';
2177
2178 if ($instance['stats_tag']['category']) {
2179
2180 $cache = &$this->__cache(__FUNCTION__, array());
2181
2182 if ( isset($cache[$p->id]) ) {
2183 return $cache[$p->id];
2184 }
2185
2186 // Try and get parent category
2187 $cats = get_the_category($p->id);
2188
2189 foreach( $cats as $cat ) {
2190 if( $cat->category_parent == 0) {
2191 $post_cat = $cat;
2192 }
2193 }
2194
2195 // Default to first category avaliable
2196 if ( $post_cat == "" && isset($cats[0]) && isset($cats[0]->slug) ) {
2197 $post_cat = $cats[0];
2198 }
2199
2200 // Build category tag
2201 if ( "" != $post_cat ) {
2202
2203 $category_id = $post_cat->term_id;
2204 $category_name = $post_cat->cat_name;
2205
2206 // WPML support, based on Serhat Evren's suggestion - see http://wordpress.org/support/topic/wpml-trick#post-5452607
2207 if ( defined('ICL_LANGUAGE_CODE') && function_exists('icl_object_id') ) {
2208 $category_id = icl_object_id( $category_id, 'category', true, ICL_LANGUAGE_CODE );
2209 $category_name = get_the_category_by_ID( $category_id );
2210 }
2211
2212 $post_cat = '<a href="' . get_category_link( $category_id ) . '" class="cat-id-' . $category_id . '">' . $category_name . '</a>';
2213
2214 }
2215
2216 return $cache[$p->id] = $post_cat;
2217
2218 }
2219
2220 return $post_cat;
2221
2222 } // end _get_post_cat
2223
2224 /**
2225 * Gets statistics data.
2226 *
2227 * @since 3.0.0
2228 * @param object p
2229 * @param array instance The current instance of the widget / shortcode parameters
2230 * @return array
2231 */
2232 protected function _get_stats($p, $instance) {
2233
2234 $cache = &$this->__cache(__FUNCTION__ . md5(json_encode($instance)), array());
2235
2236 if ( isset($cache[$p->id]) ) {
2237 return $cache[$p->id];
2238 }
2239
2240 $stats = array();
2241
2242 // STATS
2243 // comments
2244 if ($instance['stats_tag']['comment_count']) {
2245 $comments = $this->_get_comments($p, $instance);
2246
2247 $comments_text = sprintf(
2248 _n('1 comment', '%s comments', $comments, $this->plugin_slug),
2249 number_format_i18n($comments));
2250
2251 $stats[] = '<span class="wpp-comments">' . $comments_text . '</span>';
2252 }
2253
2254 // views
2255 if ($instance['stats_tag']['views']) {
2256 $pageviews = $this->_get_pageviews($p, $instance);
2257
2258 if ($instance['order_by'] == 'avg') {
2259 $views_text = sprintf(
2260 _n('1 view per day', '%s views per day', intval($pageviews), $this->plugin_slug),
2261 number_format_i18n($pageviews, 2)
2262 );
2263 }
2264 else {
2265 $views_text = sprintf(
2266 _n('1 view', '%s views', intval($pageviews), $this->plugin_slug),
2267 number_format_i18n($pageviews)
2268 );
2269 }
2270
2271 $stats[] = '<span class="wpp-views">' . $views_text . "</span>";
2272 }
2273
2274 // author
2275 if ($instance['stats_tag']['author']) {
2276 $author = $this->_get_author($p, $instance);
2277 $display_name = '<a href="' . get_author_posts_url($p->uid) . '">' . $author . '</a>';
2278 $stats[] = '<span class="wpp-author">' . sprintf(__('by %s', $this->plugin_slug), $display_name).'</span>';
2279 }
2280
2281 // date
2282 if ($instance['stats_tag']['date']['active']) {
2283 $date = $this->_get_date($p, $instance);
2284 $stats[] = '<span class="wpp-date">' . sprintf(__('posted on %s', $this->plugin_slug), $date) . '</span>';
2285 }
2286
2287 // category
2288 if ($instance['stats_tag']['category']) {
2289 $post_cat = $this->_get_post_cat($p, $instance);
2290
2291 if ($post_cat != '') {
2292 $stats[] = '<span class="wpp-category">' . sprintf(__('under %s', $this->plugin_slug), $post_cat) . '</span>';
2293 }
2294 }
2295
2296 return $cache[$p->id] = $stats;
2297
2298 } // end _get_stats
2299
2300 /**
2301 * Retrieves / creates the post thumbnail.
2302 *
2303 * @since 2.3.3
2304 * @param int id Post ID
2305 * @param string url Image URL
2306 * @param array dim Thumbnail width & height
2307 * @param string source Image source
2308 * @return string
2309 */
2310 private function __get_img($p, $id = null, $url = null, $dim = array(80, 80), $crop = true, $source = "featured", $title) {
2311
2312 if ( (!$id || empty($id) || !$this->__is_numeric($id)) && (!$url || empty($url)) ) {
2313 return $this->_render_image($this->default_thumbnail, $dim, 'wpp-thumbnail wpp_def_noID', $title);
2314 }
2315
2316 // Get image by post ID (parent)
2317 if ( $id ) {
2318 $file_path = $this->__get_image_file_paths($id, $source);
2319
2320 // No images found, return default thumbnail
2321 if ( !$file_path ) {
2322 return $this->_render_image($this->default_thumbnail, $dim, 'wpp-thumbnail wpp_def_noPath wpp_' . $source, $title);
2323 }
2324 }
2325 // Get image from URL
2326 else {
2327 // sanitize URL, just in case
2328 $image_url = esc_url( $url );
2329 // remove querystring
2330 preg_match('/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $image_url, $matches);
2331 $image_url = $matches[0];
2332
2333 $attachment_id = $this->__get_attachment_id($image_url);
2334
2335 // Image is hosted locally
2336 if ( $attachment_id ) {
2337 $file_path = get_attached_file($attachment_id);
2338 }
2339 // Image is hosted outside WordPress
2340 else {
2341 $external_image = $this->__fetch_external_image($p->id, $image_url);
2342
2343 if ( !$external_image ) {
2344 return $this->_render_image($this->default_thumbnail, $dim, 'wpp-thumbnail wpp_def_noPath wpp_no_external', $title);
2345 }
2346
2347 $file_path = $external_image;
2348 }
2349 }
2350
2351 $file_info = pathinfo($file_path);
2352
2353 // there is a thumbnail already
2354 if ( file_exists(trailingslashit($this->uploads_dir['basedir']) . $p->id . '-' . $dim[0] . 'x' . $dim[1] . '.' . $file_info['extension']) ) {
2355 return $this->_render_image( trailingslashit($this->uploads_dir['baseurl']) . $p->id . '-' . $dim[0] . 'x' . $dim[1] . '.' . $file_info['extension'], $dim, 'wpp-thumbnail wpp_cached_thumb wpp_' . $source, $title );
2356 }
2357
2358 return $this->__image_resize($p, $file_path, $dim, $crop, $source);
2359
2360 } // end __get_img
2361
2362 /**
2363 * Resizes image.
2364 *
2365 * @since 3.0.0
2366 * @param object p Post object
2367 * @param string path Image path
2368 * @param array dimension Image's width and height
2369 * @param string source Image source
2370 * @return string
2371 */
2372 private function __image_resize($p, $path, $dimension, $crop, $source) {
2373
2374 $image = wp_get_image_editor($path);
2375
2376 // valid image, create thumbnail
2377 if ( !is_wp_error($image) ) {
2378 $file_info = pathinfo($path);
2379
2380 $image->resize($dimension[0], $dimension[1], $crop);
2381 $new_img = $image->save( trailingslashit($this->uploads_dir['basedir']) . $p->id . '-' . $dimension[0] . 'x' . $dimension[1] . '.' . $file_info['extension'] );
2382
2383 if ( is_wp_error($new_img) ) {
2384 return $this->_render_image($this->default_thumbnail, $dimension, 'wpp-thumbnail wpp_imgeditor_error wpp_' . $source, '', $new_img->get_error_message());
2385 }
2386
2387 return $this->_render_image( trailingslashit($this->uploads_dir['baseurl']) . $new_img['file'], $dimension, 'wpp-thumbnail wpp_imgeditor_thumb wpp_' . $source, '');
2388 }
2389
2390 // ELSE
2391 // image file path is invalid
2392 return $this->_render_image($this->default_thumbnail, $dimension, 'wpp-thumbnail wpp_imgeditor_error wpp_' . $source, '', $image->get_error_message());
2393
2394 } // end __image_resize
2395
2396 /**
2397 * Get image absolute path / URL.
2398 *
2399 * @since 3.0.0
2400 * @param int id Post ID
2401 * @param string source Image source
2402 * @return array
2403 */
2404 private function __get_image_file_paths($id, $source) {
2405
2406 $file_path = '';
2407
2408 // get thumbnail path from the Featured Image
2409 if ($source == "featured") {
2410
2411 // thumb attachment ID
2412 $thumbnail_id = get_post_thumbnail_id($id);
2413
2414 if ($thumbnail_id) {
2415 // image path
2416 return get_attached_file($thumbnail_id);
2417 }
2418
2419 }
2420 // get thumbnail path from post content
2421 elseif ($source == "first_image") {
2422
2423 /** @var wpdb $wpdb */
2424 global $wpdb;
2425
2426 $content = $wpdb->get_results("SELECT post_content FROM {$wpdb->posts} WHERE ID = " . $id, ARRAY_A);
2427 $count = substr_count($content[0]['post_content'], '<img');
2428
2429 // images have been found
2430 // TODO: try to merge these conditions into one IF.
2431 if ($count > 0) {
2432
2433 preg_match_all('/<img.+src=[\'"]([^\'"]+)[\'"].*>/i', $content[0]['post_content'], $content_images);
2434
2435 if (isset($content_images[1][0])) {
2436 $attachment_id = $this->__get_attachment_id($content_images[1][0]);
2437
2438 // image from Media Library
2439 if ($attachment_id) {
2440 $file_path = get_attached_file($attachment_id);
2441 // There's a file path, so return it
2442 if ( !empty($file_path) )
2443 return $file_path;
2444 } // external image?
2445 else {
2446 $external_image = $this->__fetch_external_image($id, $content_images[1][0]);
2447 if ( $external_image ) {
2448 return $external_image;
2449 }
2450 }
2451 }
2452 }
2453
2454 }
2455
2456 return false;
2457
2458 } // end __get_image_file_paths
2459
2460 /**
2461 * Render image tag.
2462 *
2463 * @since 3.0.0
2464 * @param string src Image URL
2465 * @param array dimension Image's width and height
2466 * @param string class CSS class
2467 * @param string title Image's title/alt attribute
2468 * @param string error Error, if the image could not be created
2469 * @return string
2470 */
2471 protected function _render_image($src, $dimension, $class, $title = "", $error = null) {
2472
2473 $msg = '';
2474
2475 if ($error) {
2476 $msg = '<!-- ' . $error . ' --> ';
2477 }
2478
2479 return $msg .
2480 '<img src="' . $src . '" title="' . esc_attr($title) . '" alt="' . esc_attr($title) . '" width="' . $dimension[0] . '" height="' . $dimension[1] . '" class="' . $class . '" />';
2481
2482 } // _render_image
2483
2484 /**
2485 * Get the Attachment ID for a given image URL.
2486 *
2487 * @since 3.0.0
2488 * @author Frankie Jarrett
2489 * @link http://frankiejarrett.com/get-an-attachment-id-by-url-in-wordpress/
2490 * @param string url
2491 * @return bool|int
2492 */
2493 private function __get_attachment_id($url) {
2494
2495 // Split the $url into two parts with the wp-content directory as the separator.
2496 $parse_url = explode( parse_url( WP_CONTENT_URL, PHP_URL_PATH ), $url );
2497
2498 // Get the host of the current site and the host of the $url, ignoring www.
2499 $this_host = str_ireplace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) );
2500 $file_host = str_ireplace( 'www.', '', parse_url( $url, PHP_URL_HOST ) );
2501
2502 // Return nothing if there aren't any $url parts or if the current host and $url host do not match.
2503 if ( ! isset( $parse_url[1] ) || empty( $parse_url[1] ) || ( $this_host != $file_host ) ) {
2504 return false;
2505 }
2506
2507 // Now we're going to quickly search the DB for any attachment GUID with a partial path match.
2508 // Example: /uploads/2013/05/test-image.jpg
2509 global $wpdb;
2510
2511 if ( !$attachment = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1] ) ) ) {
2512 // Maybe it's a resized image, so try to get the full one
2513 $parse_url[1] = preg_replace( '/-[0-9]{1,4}x[0-9]{1,4}\.(jpg|jpeg|png|gif|bmp)$/i', '.$1', $parse_url[1] );
2514 $attachment = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1] ) );
2515 }
2516
2517 // Returns null if no attachment is found.
2518 return $attachment[0];
2519
2520 } // __get_attachment_id
2521
2522 /**
2523 * Fetchs external images.
2524 *
2525 * @since 2.3.3
2526 * @param string url
2527 * @return bool|int
2528 */
2529 private function __fetch_external_image($id, $url){
2530
2531 $full_image_path = trailingslashit( $this->uploads_dir['basedir'] ) . "{$id}_". sanitize_file_name( rawurldecode(wp_basename( $url )) );
2532
2533 // if the file exists already, return URL and path
2534 if ( file_exists($full_image_path) )
2535 return $full_image_path;
2536
2537 $accepted_status_codes = array( 200, 301, 302 );
2538 $response = wp_remote_head( $url, array( 'timeout' => 5, 'sslverify' => false ) );
2539
2540 if ( !is_wp_error($response) && in_array(wp_remote_retrieve_response_code($response), $accepted_status_codes) ) {
2541
2542 if ( function_exists('exif_imagetype') ) {
2543 $image_type = exif_imagetype( $url );
2544 } else {
2545 $image_type = getimagesize( $url );
2546 $image_type = ( isset($image_type[2]) ) ? $image_type[2] : NULL;
2547 }
2548
2549 if ( in_array($image_type, array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG)) ) {
2550 require_once( ABSPATH . 'wp-admin/includes/file.php' );
2551
2552 $url = str_replace( 'https://', 'http://', $url );
2553 $tmp = download_url( $url );
2554
2555 // move file to Uploads
2556 if ( !is_wp_error( $tmp ) && rename($tmp, $full_image_path) ) {
2557 // borrowed from WP - set correct file permissions
2558 $stat = stat( dirname( $full_image_path ));
2559 $perms = $stat['mode'] & 0000644;
2560 @chmod( $full_image_path, $perms );
2561
2562 return $full_image_path;
2563 }
2564 }
2565 }
2566
2567 return false;
2568
2569 } // end __fetch_external_image
2570
2571 /**
2572 * Builds post's excerpt
2573 *
2574 * @since 1.4.6
2575 * @global object wpdb
2576 * @param int post ID
2577 * @param array widget instance
2578 * @return string
2579 */
2580 protected function _get_summary($id, $instance){
2581
2582 if ( !$this->__is_numeric($id) )
2583 return false;
2584
2585 global $wpdb;
2586
2587 $excerpt = "";
2588
2589 // WPML support, get excerpt for current language
2590 if ( defined('ICL_LANGUAGE_CODE') && function_exists('icl_object_id') ) {
2591 $current_id = icl_object_id( $id, get_post_type( $id ), true, ICL_LANGUAGE_CODE );
2592
2593 $the_post = get_post( $current_id );
2594 $excerpt = ( empty($the_post->post_excerpt) )
2595 ? $the_post->post_content
2596 : $the_post->post_excerpt;
2597 } // Use ol' plain excerpt
2598 else {
2599 $the_post = get_post( $id );
2600 $excerpt = ( empty($the_post->post_excerpt) )
2601 ? $the_post->post_content
2602 : $the_post->post_excerpt;
2603
2604 // RRR added call to the_content filters, allows qTranslate to hook in.
2605 if ( $this->qTrans )
2606 $excerpt = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $excerpt );
2607 }
2608
2609 // remove caption tags
2610 $excerpt = preg_replace( "/\[caption.*\[\/caption\]/", "", $excerpt );
2611
2612 // remove Flash objects
2613 $excerpt = preg_replace( "/<object[0-9 a-z_?*=\":\-\/\.#\,\\n\\r\\t]+/smi", "", $excerpt );
2614
2615 // remove Iframes
2616 $excerpt = preg_replace( "/<iframe.*?\/iframe>/i", "", $excerpt);
2617
2618 // remove WP shortcodes
2619 $excerpt = strip_shortcodes( $excerpt );
2620
2621 // remove HTML tags if requested
2622 if ( $instance['post-excerpt']['keep_format'] ) {
2623 $excerpt = strip_tags($excerpt, '<a><b><i><em><strong>');
2624 } else {
2625 $excerpt = strip_tags($excerpt);
2626 // remove URLs, too
2627 $excerpt = preg_replace( '_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS', '', $excerpt );
2628 }
2629
2630 // Fix RSS CDATA tags
2631 $excerpt = str_replace( ']]>', ']]&gt;', $excerpt );
2632
2633 // do we still have something to display?
2634 if ( !empty($excerpt) ) {
2635
2636 // truncate excerpt
2637 if ( isset($instance['post-excerpt']['words']) && $instance['post-excerpt']['words'] ) { // by words
2638
2639 $words = explode(" ", $excerpt, $instance['post-excerpt']['length'] + 1);
2640
2641 if ( count($words) > $instance['post-excerpt']['length'] ) {
2642 array_pop($words);
2643 $excerpt = implode(" ", $words) . "...";
2644 }
2645
2646 } else { // by characters
2647
2648 if ( strlen($excerpt) > $instance['post-excerpt']['length'] ) {
2649 $excerpt = mb_substr( $excerpt, 0, $instance['post-excerpt']['length'], $this->charset ) . "...";
2650 }
2651
2652 }
2653
2654 }
2655
2656 // Balance tags, if needed
2657 if ( $instance['post-excerpt']['keep_format'] ) {
2658 $excerpt = force_balance_tags($excerpt);
2659 }
2660
2661 return $excerpt;
2662
2663 } // _get_summary
2664
2665 /**
2666 * WPP shortcode handler
2667 * Since 2.0.0
2668 */
2669 public function shortcode($atts = null, $content = null) {
2670 /**
2671 * @var String $header
2672 * @var Int $limit
2673 * @var String $range
2674 * @var Bool $freshness
2675 * @var String $order_by
2676 * @var String $post_type
2677 * @var String $pid
2678 * @var String $cat
2679 * @var String $author
2680 * @var Int $title_length
2681 * @var Int $title_by_words
2682 * @var Int $excerpt_length
2683 * @var Int $excerpt_format
2684 * @var Int $excerpt_by_words
2685 * @var Int $thumbnail_width
2686 * @var Int $thumbnail_height
2687 * @var Bool $rating
2688 * @var Bool $stats_comments
2689 * @var Bool $stats_views
2690 * @var Bool $stats_author
2691 * @var Bool $stats_date
2692 * @var String $stats_date_format
2693 * @var Bool $stats_category
2694 * @var String $wpp_start
2695 * @var String $wpp_end
2696 * @var String $header_start
2697 * @var String $header_end
2698 * @var String $post_html
2699 * @var Bool $php
2700 */
2701 extract( shortcode_atts( array(
2702 'header' => '',
2703 'limit' => 10,
2704 'range' => 'daily',
2705 'freshness' => false,
2706 'order_by' => 'views',
2707 'post_type' => 'post,page',
2708 'pid' => '',
2709 'cat' => '',
2710 'author' => '',
2711 'title_length' => 0,
2712 'title_by_words' => 0,
2713 'excerpt_length' => 0,
2714 'excerpt_format' => 0,
2715 'excerpt_by_words' => 0,
2716 'thumbnail_width' => 0,
2717 'thumbnail_height' => 0,
2718 'rating' => false,
2719 'stats_comments' => false,
2720 'stats_views' => true,
2721 'stats_author' => false,
2722 'stats_date' => false,
2723 'stats_date_format' => 'F j, Y',
2724 'stats_category' => false,
2725 'wpp_start' => '<ul class="wpp-list">',
2726 'wpp_end' => '</ul>',
2727 'header_start' => '<h2>',
2728 'header_end' => '</h2>',
2729 'post_html' => '',
2730 'php' => false
2731 ),$atts));
2732
2733 // possible values for "Time Range" and "Order by"
2734 $range_values = array("yesterday", "daily", "weekly", "monthly", "all");
2735 $order_by_values = array("comments", "views", "avg");
2736
2737 $shortcode_ops = array(
2738 'title' => strip_tags($header),
2739 'limit' => (!empty($limit) && $this->__is_numeric($limit) && $limit > 0) ? $limit : 10,
2740 'range' => (in_array($range, $range_values)) ? $range : 'daily',
2741 'freshness' => empty($freshness) ? false : $freshness,
2742 'order_by' => (in_array($order_by, $order_by_values)) ? $order_by : 'views',
2743 'post_type' => empty($post_type) ? 'post,page' : $post_type,
2744 'pid' => preg_replace('|[^0-9,]|', '', $pid),
2745 'cat' => preg_replace('|[^0-9,-]|', '', $cat),
2746 'author' => preg_replace('|[^0-9,]|', '', $author),
2747 'shorten_title' => array(
2748 'active' => (!empty($title_length) && $this->__is_numeric($title_length) && $title_length > 0),
2749 'length' => (!empty($title_length) && $this->__is_numeric($title_length)) ? $title_length : 0,
2750 'words' => (!empty($title_by_words) && $this->__is_numeric($title_by_words) && $title_by_words > 0),
2751 ),
2752 'post-excerpt' => array(
2753 'active' => (!empty($excerpt_length) && $this->__is_numeric($excerpt_length) && ($excerpt_length > 0)),
2754 'length' => (!empty($excerpt_length) && $this->__is_numeric($excerpt_length)) ? $excerpt_length : 0,
2755 'keep_format' => (!empty($excerpt_format) && $this->__is_numeric($excerpt_format) && ($excerpt_format > 0)),
2756 'words' => (!empty($excerpt_by_words) && $this->__is_numeric($excerpt_by_words) && $excerpt_by_words > 0),
2757 ),
2758 'thumbnail' => array(
2759 'active' => (!empty($thumbnail_width) && $this->__is_numeric($thumbnail_width) && $thumbnail_width > 0),
2760 'width' => (!empty($thumbnail_width) && $this->__is_numeric($thumbnail_width) && $thumbnail_width > 0) ? $thumbnail_width : 0,
2761 'height' => (!empty($thumbnail_height) && $this->__is_numeric($thumbnail_height) && $thumbnail_height > 0) ? $thumbnail_height : 0,
2762 ),
2763 'rating' => empty($rating) ? false : $rating,
2764 'stats_tag' => array(
2765 'comment_count' => empty($stats_comments) ? false : $stats_comments,
2766 'views' => empty($stats_views) ? false : $stats_views,
2767 'author' => empty($stats_author) ? false : $stats_author,
2768 'date' => array(
2769 'active' => empty($stats_date) ? false : $stats_date,
2770 'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
2771 ),
2772 'category' => empty($stats_category) ? false : $stats_category,
2773 ),
2774 'markup' => array(
2775 'custom_html' => true,
2776 'wpp-start' => empty($wpp_start) ? '<ul class="wpp-list">' : $wpp_start,
2777 'wpp-end' => empty($wpp_end) ? '</ul>' : $wpp_end,
2778 'title-start' => empty($header_start) ? '' : $header_start,
2779 'title-end' => empty($header_end) ? '' : $header_end,
2780 'post-html' => empty($post_html) ? '<li>{thumb} {title} {stats}</li>' : $post_html
2781 )
2782 );
2783
2784 $shortcode_content = "\n". "<!-- WordPress Popular Posts Plugin v". $this->version ." [" . ( $php ? "PHP" : "SC" ) . "] [".$shortcode_ops['range']."] [".$shortcode_ops['order_by']."] [custom]" . ( !empty($shortcode_ops['pid']) ? " [PID]" : "" ) . ( !empty($shortcode_ops['cat']) ? " [CAT]" : "" ) . ( !empty($shortcode_ops['author']) ? " [UID]" : "" ) . " -->"."\n";
2785
2786 // is there a title defined by user?
2787 if (!empty($header) && !empty($header_start) && !empty($header_end)) {
2788 $shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . apply_filters('widget_title', $header) . htmlspecialchars_decode($header_end, ENT_QUOTES);
2789 }
2790
2791 // print popular posts list
2792 $shortcode_content .= ( $php ) ? $this->__get_popular_posts($shortcode_ops) : htmlspecialchars_decode( $this->__get_popular_posts($shortcode_ops), ENT_QUOTES ); // WP's editor converts shortcode parameters into entities
2793 $shortcode_content .= "\n". "<!-- End WordPress Popular Posts Plugin v". $this->version ." -->"."\n";
2794
2795 return $shortcode_content;
2796
2797 } // end shortcode
2798
2799 /**
2800 * Parses content tags
2801 *
2802 * @since 1.4.6
2803 * @param string HTML string with content tags
2804 * @param array Post data
2805 * @param bool Used to display post rating (if functionality is available)
2806 * @return string
2807 */
2808 private function __format_content($string, $data = array(), $rating) {
2809
2810 if (empty($string) || (empty($data) || !is_array($data)))
2811 return false;
2812
2813 $params = array();
2814 $pattern = '/\{(excerpt|summary|stats|title|image|thumb|thumb_img|rating|score|url|text_title|author|category|views|comments|date)\}/i';
2815 preg_match_all($pattern, $string, $matches);
2816
2817 array_map('strtolower', $matches[0]);
2818
2819 if ( in_array("{title}", $matches[0]) ) {
2820 $string = str_replace( "{title}", $data['title'], $string );
2821 }
2822
2823 if ( in_array("{stats}", $matches[0]) ) {
2824 $string = str_replace( "{stats}", $data['stats'], $string );
2825 }
2826
2827 if ( in_array("{excerpt}", $matches[0]) ) {
2828 $string = str_replace( "{excerpt}", htmlentities($data['summary'], ENT_QUOTES, $this->charset), $string );
2829 }
2830
2831 if ( in_array("{summary}", $matches[0]) ) {
2832 $string = str_replace( "{summary}", htmlentities($data['summary'], ENT_QUOTES, $this->charset), $string );
2833 }
2834
2835 if ( in_array("{image}", $matches[0]) ) {
2836 $string = str_replace( "{image}", $data['img'], $string );
2837 }
2838
2839 if ( in_array("{thumb}", $matches[0]) ) {
2840 $string = str_replace( "{thumb}", $data['img'], $string );
2841 }
2842
2843 if ( in_array("{thumb_img}", $matches[0]) ) {
2844 $string = str_replace( "{thumb_img}", $data['img_no_link'], $string );
2845 }
2846
2847 // WP-PostRatings check
2848 if ( $rating ) {
2849 if ( function_exists('the_ratings_results') && in_array("{rating}", $matches[0]) ) {
2850 $string = str_replace( "{rating}", the_ratings_results($data['id']), $string );
2851 }
2852
2853 if ( function_exists('expand_ratings_template') && in_array("{score}", $matches[0]) ) {
2854 $string = str_replace( "{score}", expand_ratings_template('%RATINGS_SCORE%', $data['id']), $string);
2855 // removing the redundant plus sign
2856 $string = str_replace('+', '', $string);
2857 }
2858 }
2859
2860 if ( in_array("{url}", $matches[0]) ) {
2861 $string = str_replace( "{url}", $data['url'], $string );
2862 }
2863
2864 if ( in_array("{text_title}", $matches[0]) ) {
2865 $string = str_replace( "{text_title}", $data['text_title'], $string );
2866 }
2867
2868 if ( in_array("{author}", $matches[0]) ) {
2869 $string = str_replace( "{author}", $data['author'], $string );
2870 }
2871
2872 if ( in_array("{category}", $matches[0]) ) {
2873 $string = str_replace( "{category}", $data['category'], $string );
2874 }
2875
2876 if ( in_array("{views}", $matches[0]) ) {
2877 $string = str_replace( "{views}", $data['views'], $string );
2878 }
2879
2880 if ( in_array("{comments}", $matches[0]) ) {
2881 $string = str_replace( "{comments}", $data['comments'], $string );
2882 }
2883
2884 if ( in_array("{date}", $matches[0]) ) {
2885 $string = str_replace( "{date}", $data['date'], $string );
2886 }
2887
2888 return $string;
2889
2890 } // end __format_content
2891
2892 /**
2893 * Returns HTML list via AJAX
2894 *
2895 * @since 2.3.3
2896 * @return string
2897 */
2898 public function get_popular( ) {
2899
2900 if ( $this->__is_numeric($_GET['id']) && ($_GET['id'] != '') ) {
2901 $id = $_GET['id'];
2902 } else {
2903 die("Invalid ID");
2904 }
2905
2906 $widget_instances = $this->get_settings();
2907
2908 if ( isset($widget_instances[$id]) ) {
2909
2910 echo $this->__get_popular_posts( $widget_instances[$id] );
2911
2912 } else {
2913
2914 echo "Invalid Widget ID";
2915 }
2916
2917 exit();
2918
2919 } // end get_popular
2920
2921 /*--------------------------------------------------*/
2922 /* Helper functions
2923 /*--------------------------------------------------*/
2924
2925 /**
2926 * Gets list of available thumbnails sizes
2927 *
2928 * @since 3.2.0
2929 * @link http://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
2930 * @param string $size
2931 * @return array|bool
2932 */
2933 private function __get_image_sizes( $size = '' ) {
2934
2935 global $_wp_additional_image_sizes;
2936
2937 $sizes = array();
2938 $get_intermediate_image_sizes = get_intermediate_image_sizes();
2939
2940 // Create the full array with sizes and crop info
2941 foreach( $get_intermediate_image_sizes as $_size ) {
2942
2943 if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
2944
2945 $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
2946 $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
2947 $sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
2948
2949 } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
2950
2951 $sizes[ $_size ] = array(
2952 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
2953 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
2954 'crop' => $_wp_additional_image_sizes[ $_size ]['crop']
2955 );
2956
2957 }
2958
2959 }
2960
2961 // Get only 1 size if found
2962 if ( $size ) {
2963
2964 if( isset( $sizes[ $size ] ) ) {
2965 return $sizes[ $size ];
2966 } else {
2967 return false;
2968 }
2969
2970 }
2971
2972 return $sizes;
2973 }
2974
2975 /**
2976 * Gets post/page ID if current page is singular
2977 *
2978 * @since 3.1.2
2979 */
2980 public function is_single() {
2981 if ( (is_single() || is_page()) && !is_front_page() && !is_preview() && !is_trackback() && !is_feed() && !is_robots() ) {
2982 global $post;
2983 $this->current_post_id = ( is_object($post) ) ? $post->ID : 0;
2984 } else {
2985 $this->current_post_id = 0;
2986 }
2987 } // end is_single
2988
2989 /**
2990 * Checks for valid number
2991 *
2992 * @since 2.1.6
2993 * @param int number
2994 * @return bool
2995 */
2996 private function __is_numeric($number){
2997 return !empty($number) && is_numeric($number) && (intval($number) == floatval($number));
2998 }
2999
3000 /**
3001 * Returns server datetime
3002 *
3003 * @since 2.1.6
3004 * @return string
3005 */
3006 private function __curdate() {
3007 return gmdate( 'Y-m-d', ( time() + ( get_site_option( 'gmt_offset' ) * 3600 ) ));
3008 } // end __curdate
3009
3010 /**
3011 * Returns mysql datetime
3012 *
3013 * @since 2.1.6
3014 * @return string
3015 */
3016 private function __now() {
3017 return current_time('mysql');
3018 } // end __now
3019
3020 /**
3021 * Returns time
3022 *
3023 * @since 2.3.0
3024 * @return string
3025 */
3026 private function __microtime_float() {
3027
3028 list( $msec, $sec ) = explode( ' ', microtime() );
3029
3030 $microtime = (float) $msec + (float) $sec;
3031 return $microtime;
3032
3033 } // end __microtime_float
3034
3035 /**
3036 * Compares values
3037 *
3038 * @since 2.3.4
3039 * @param int a
3040 * @param int b
3041 * @return int
3042 */
3043 private function __sorter($a, $b) {
3044
3045 if ($a > 0 && $b > 0) {
3046 return $a - $b;
3047 } else {
3048 return $b - $a;
3049 }
3050
3051 } // end __sorter
3052
3053 /**
3054 * Merges two associative arrays recursively
3055 *
3056 * @since 2.3.4
3057 * @link http://www.php.net/manual/en/function.array-merge-recursive.php#92195
3058 * @param array array1
3059 * @param array array2
3060 * @return array
3061 */
3062 private function __merge_array_r( array &$array1, array &$array2 ) {
3063
3064 $merged = $array1;
3065
3066 foreach ( $array2 as $key => &$value ) {
3067
3068 if ( is_array( $value ) && isset ( $merged[$key] ) && is_array( $merged[$key] ) ) {
3069 $merged[$key] = $this->__merge_array_r( $merged[$key], $value );
3070 } else {
3071 $merged[$key] = $value;
3072 }
3073 }
3074
3075 return $merged;
3076
3077 } // end __merge_array_r
3078
3079 /**
3080 * Checks if visitor is human or bot.
3081 *
3082 * @since 3.0.0
3083 * @return bool FALSE if human, TRUE if bot
3084 */
3085 private function __is_bot() {
3086
3087 if ( !isset($_SERVER['HTTP_USER_AGENT']) || empty($_SERVER['HTTP_USER_AGENT']) )
3088 return true; // No UA? Bot (probably)
3089
3090 $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
3091
3092 foreach ( $this->botlist as $bot ) {
3093 if ( false !== strpos($user_agent, $bot) ) {
3094 return true; // Bot
3095 }
3096 }
3097
3098 return false; // Human, I guess...
3099
3100 } // end __is_bot
3101
3102 /**
3103 * Debug function.
3104 *
3105 * @since 3.0.0
3106 * @param mixed $v variable to display with var_dump()
3107 * @param mixed $v,... unlimited optional number of variables to display with var_dump()
3108 */
3109 private function __debug($v) {
3110
3111 if ( !defined('WPP_DEBUG') || !WPP_DEBUG )
3112 return;
3113
3114 foreach (func_get_args() as $arg) {
3115
3116 print "<pre>";
3117 var_dump($arg);
3118 print "</pre>";
3119
3120 }
3121
3122 } // end __debug
3123
3124 } // end class
3125
3126 }
3127
3128 /**
3129 * WordPress Popular Posts template tags for use in themes.
3130 */
3131
3132 /**
3133 * Template tag - gets views count.
3134 *
3135 * @since 2.0.3
3136 * @global object wpdb
3137 * @param int id
3138 * @param string range
3139 * @param bool number_format
3140 * @return string
3141 */
3142 function wpp_get_views($id = NULL, $range = NULL, $number_format = true) {
3143
3144 // have we got an id?
3145 if ( empty($id) || is_null($id) || !is_numeric($id) ) {
3146 return "-1";
3147 } else {
3148 global $wpdb;
3149
3150 $table_name = $wpdb->prefix . "popularposts";
3151
3152 if ( !$range || 'all' == $range ) {
3153 $query = "SELECT pageviews FROM {$table_name}data WHERE postid = '{$id}'";
3154 } else {
3155 $interval = "";
3156
3157 switch( $range ){
3158 case "yesterday":
3159 $interval = "1 DAY";
3160 break;
3161
3162 case "daily":
3163 $interval = "1 DAY";
3164 break;
3165
3166 case "weekly":
3167 $interval = "1 WEEK";
3168 break;
3169
3170 case "monthly":
3171 $interval = "1 MONTH";
3172 break;
3173
3174 default:
3175 $interval = "1 DAY";
3176 break;
3177 }
3178
3179 $now = current_time('mysql');
3180
3181 $query = "SELECT SUM(pageviews) FROM {$table_name}summary WHERE postid = '{$id}' AND last_viewed > DATE_SUB('{$now}', INTERVAL {$interval}) LIMIT 1;";
3182 }
3183
3184 $result = $wpdb->get_var($query);
3185
3186 if ( !$result ) {
3187 return "0";
3188 }
3189
3190 return ($number_format) ? number_format_i18n( intval($result) ) : $result;
3191 }
3192
3193 }
3194
3195 /**
3196 * Template tag - gets popular posts.
3197 *
3198 * @since 2.0.3
3199 * @param mixed args
3200 */
3201 function wpp_get_mostpopular($args = NULL) {
3202
3203 $shortcode = '[wpp';
3204
3205 if ( is_null( $args ) ) {
3206 $shortcode .= ']';
3207 } else {
3208 if( is_array( $args ) ){
3209 $atts = '';
3210 foreach( $args as $key => $arg ){
3211 $atts .= ' ' . $key . '="' . htmlspecialchars($arg, ENT_QUOTES, $encoding = ini_get("default_charset"), false) . '"';
3212 }
3213 } else {
3214 $atts = trim( str_replace( "&", " ", $args ) );
3215 }
3216
3217 $shortcode .= ' ' . $atts . ' php=true]';
3218 }
3219
3220 echo do_shortcode( $shortcode );
3221
3222 }
3223
3224 /**
3225 * Template tag - gets popular posts. Deprecated in 2.0.3, use wpp_get_mostpopular instead.
3226 *
3227 * @since 1.0
3228 * @param mixed args
3229 */
3230 function get_mostpopular($args = NULL) {
3231 trigger_error( 'The get_mostpopular() has been deprecated since 2.0.3. Please use wpp_get_mostpopular() instead.', E_USER_WARNING );
3232 return wpp_get_mostpopular($args);
3233 }
3234