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