Plugin Directory

Changeset 1989517


Ignore:
Timestamp:
12/08/2018 08:54:34 PM (7 years ago)
Author:
johnalarcon
Message:

Remove breaking code and notify end user of eminent re-release.

Location:
plugin-stats/trunk
Files:
4 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • plugin-stats/trunk/plugin-stats.php

    r1988624 r1989517  
    11<?php
    2 /*
     2
     3/**
     4 * -----------------------------------------------------------------------------
    35 * Plugin Name: Plugin Stats
    4  * Plugin URI: http://scompt.com/projects/plugin-stats
    5  * Description: Plugin Stats provides a shortcode, template function, and dashboard widget which graphs the downloads completed for plugins hosted at WordPress.org.
    6  * Author: Edward Dale
    7  * Version: 1.1
    8  * Author URI: http://scompt.com
     6 * Description: Provides plugin developers a simple hub from which they can keep track of the stats on their own (or other developers') plugins.
     7 * Version: 2.0
     8 * Author: John Alarcon
     9 * Author URI: https://johnalarcon.com
     10 * Text Domain: plugin-stats
     11 * Domain Path: /languages
     12 * -----------------------------------------------------------------------------
     13 * This is free software released under the terms of the General Public License,
     14 * version 2, or later. It is distributed WITHOUT ANY WARRANTY; without even the
     15 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Full
     16 * text of the license is available at https://www.gnu.org/licenses/gpl-2.0.txt.
     17 * -----------------------------------------------------------------------------
     18 * A special thanks to Edward Dale (scompt.com) who graciously changed ownership
     19 * of this plugin at the WordPress plugin repository, so it could be redeveloped
     20 * into a new plugin.
     21 * -----------------------------------------------------------------------------
     22 * Copyright © 2018 - John Alarcon
     23 * -----------------------------------------------------------------------------
    924 */
    10  
     25
    1126/**
    12  * Plugin Stats provides a shortcode, template function, and dashboard widget
    13  * which graphs the downloads completed for plugins hosted at WordPress.org.
     27 * The primary plugin class.
    1428 *
    15  * LICENSE
    16  * This file is part of Plugin Stats.
     29 * @author John Alarcon
    1730 *
    18  * Plugin Stats is free software; you can redistribute it and/or
    19  * modify it under the terms of the GNU General Public License
    20  * as published by the Free Software Foundation; either version 2
    21  * of the License, or (at your option) any later version.
    22  *
    23  * This program is distributed in the hope that it will be useful,
    24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    26  * GNU General Public License for more details.
    27  *
    28  * You should have received a copy of the GNU General Public License
    29  * along with this program; if not, write to the Free Software
    30  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    31  *
    32  * @package    Plugin Stats
    33  * @author     Edward Dale <[email protected]>
    34  * @copyright  Copyright 2008 Edward Dale
    35  * @license    http://www.gnu.org/licenses/gpl.txt GPL 2.0
    36  * @version    $Id: immerstat.php 45292 2008-05-11 19:20:29Z scompt $
    37  * @link       http://www.scompt.com/projects/plugin-stats
    38  * @since      1.0
    3931 */
    40  
    41 // Adding a comment, so this will refresh the listing.
    42  
    4332class PluginStats {
    44     var $snoop;   // For grabbing the xml from WordPress.org
    45    
    46     /**
    47      * Adds hooks for the shortcode, template function, and initialization
    48      */
    49     function PluginStats() {
    50         add_action('admin_init', array(&$this, 'init'));
    51         add_shortcode('plugin-stats', array(&$this, 'shortcode'));
    52         add_action('plugin-stats', array(&$this, 'template_function'));
    53     }
    54    
    55     /**
    56      * Adds dashboard filters and loads translations.
    57      */
    58     function init() {
    59         add_action('wp_dashboard_setup', array(&$this, 'wp_dashboard_setup'));
    60         add_filter( 'wp_dashboard_widgets', array(&$this, 'add_widget') );
    61         add_filter('dashwidman_safewidgets', array(&$this, 'add_safe_widget'));
    6233
    63         load_plugin_textdomain('plugin-stats', str_replace(ABSPATH, '', dirname(__FILE__)));       
    64     }
    65    
    66     /**
    67      * Action hook for the plugin-stats template function.
    68      */
    69     function template_function($slugname='default', $size='360x100', $addlink='0') {
    70         echo $this->shortcode(array('slugname'=>$slugname, 'size'=>$size, 'addlink'=>$addlink));
    71     }
    72    
    73     /**
    74      * Handler for the plugin-stats shortcode.
    75      */
    76     function shortcode($atts) {
    77         extract(shortcode_atts(array(
    78             'slugname' => 'default',
    79             'size' => '360x100',
    80             'addlink' => '0',
    81         ), $atts));
     34    /**
     35     * Object instance.
     36     *
     37     * @var null|object Current or new instance of the object.
     38     *
     39     * @since 2.0.0
     40     */
     41    private static $instance = null;
    8242
    83         $slugname = preg_replace('/[^a-zA-Z0-9_-]/', '', $slugname);
    84         if( $addlink ) add_filter('plugin-stats_img-link', array(&$this, 'add_link'), 10, 2);
    85         $imgLink = $this->getImageLink($slugname, $size);
    86         remove_filter('plugin-stats_img-link', array(&$this, 'add_link'), 10, 2);
    87        
    88         return $imgLink;
    89     }
     43    /**
     44     * Simple constructor.
     45     *
     46     * @author John Alarcon
     47     *
     48     * @since 2.0.0
     49     */
     50    private function __construct() {
    9051
    91     /////////////////////////
    92     // Dashboard functions //
    93     /////////////////////////
    94    
    95     /**
    96      * Register the widget and control function.
    97      */
    98     function wp_dashboard_setup() {
    99         wp_register_sidebar_widget( 'plugin-stats', __( 'Plugin Stats', 'plugin-stats'), 'wp_dashboard_empty', array(), array(&$this, 'widget_output')  );
    100         wp_register_widget_control( 'plugin-stats', __( 'Plugin Stats', 'plugin-stats' ),  array(&$this, 'widget_control'), array(), array( 'widget_id' => 'plugin-stats' ) );
    101     }
    102    
    103     /**
    104      * Handles the 'edit' side of the widget.
    105      */
    106     function widget_control() {
    107         global $user_ID;
     52        $this->init();
    10853
    109         if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['plugin-stats_slugs']) ) {
    110             // sanitize the plugin names and save them
    111             $slugs = $_POST['plugin-stats_slugs'];
    112             $slugs = preg_replace('/[^a-zA-Z0-9_,-]/', '', $slugs);
    113             update_usermeta($user_ID, 'plugin-stats_slugs', $slugs);
    114         } else {
    115             // display the plugin names
    116             $slugs = get_usermeta($user_ID, 'plugin-stats_slugs');
    117             ?>
    118             <p>
    119                 <label for="plugin-stats_slugs"><?php _e('Enter a comma-separated list of plugin slugs.', 'plugin-stats' ); ?>
    120                     <input id="plugin-stats_slugs" name="plugin-stats_slugs" type="text" value="<?php echo $slugs ?>" />
    121                 </label>
    122             </p>
    123             <?php
    124         }
    125     }
    126    
    127     /**
    128      * Handles the output side of the widget
    129      */
    130     function widget_output() {
    131         global $user_ID;
     54    }
    13255
    133         // grab the plugins and split 'em, should already be sanitized
    134         $slugs = get_usermeta($user_ID, 'plugin-stats_slugs');
    135         if( empty($slugs) ) {
    136             _e('Thanks for installing the <a href="http://scompt.com/projects/plugin-stats">Plugin Stats</a> plugin!  <a href="index.php?edit=plugin-stats#plugin-stats">Add some plugins</a> to start tracking your downloads in your dashboard.', 'plugin-stats');
    137             return;
    138         }
    139         $slugs = explode(',', $slugs);
    140        
    141         // We want the dashboard output linked
    142         add_filter('plugin-stats_img-link', array(&$this, 'add_link'), 10, 2);
     56    /**
     57     * Setup filters and actions.
     58     *
     59     * @author John Alarcon
     60     *
     61     * @since 2.0.0
     62     */
     63    public function init() {
    14364
    144         foreach( $slugs as $slug ) {
    145             echo $this->getImageLink($slug, '360x100');
    146         }
    147     }
    148    
    149     /**
    150      * Add widget to the current widgets.
    151      */
    152     function add_widget( $widgets ) {
    153         global $wp_registered_widgets;
    154         array_splice( $widgets, 0, 0, 'plugin-stats' );
    155         return $widgets;
     65        // Add a notice to the dashboard.
     66        add_action('admin_notices', [$this, 'print_admin_notice']);
     67
    15668    }
    157    
    158     /**
    159      * Mark the plugin as safe for http://wordpress.org/extend/plugins/dashboard-widget-manager/
    160      */
    161     function add_safe_widget($safe) {
    162         $safe[]='plugin-stats';
    163         return $safe;
    164     }
    165    
    166     /**
    167      * Makes a link to the stats page on WP.org out of the provided image link.
    168      */
    169     function add_link($imgLink, $slugname) {
    170         return "<a href='http://wordpress.org/extend/plugins/$slugname/stats/'>$imgLink</a>";
     69
     70    /**
     71     * Print admin notice.
     72     *
     73     *
     74     * @author John Alarcon
     75     *
     76     * @since 2.0.0
     77     */
     78    public function print_admin_notice() {
     79
     80        // Check if user has already been notified.
     81        $notified = get_option('plugin_stats_notified', false);
     82
     83        // Already notified? Don't be a nag.
     84        if ($notified) {
     85            return;
     86        }
     87
     88        // Contain and print the message.
     89        echo '<div class="notice notice-error is-dismissible"><p>The <strong>Plugin Stats</strong> plugin is under redevelopment and will be re-released soon!</p><p>If you want to use the previous version, you can download it <a href="https://plugins.trac.wordpress.org/browser/plugin-stats/tags/1.1">here</a> – be sure not to update it afterward or you will be right back here!</p><p>This message will not be shown again.</p></div>';
     90
     91        // Indicate user has been notified.
     92        update_option('plugin_stats_notified', 1);
     93
    17194    }
    172    
     95
    17396    /**
    174      * Builds an image link out of the stats that have already been retrieved
     97     * Get current instance of this object, or create and return a new instance.
     98     *
     99     * @author John Alarcon
     100     *
     101     * @since 0.1.0
     102     *
     103     * @return object The current instance of the object.
    175104     */
    176     function getImageLink($slugname, $imgSize) {
    177         global $wp_locale;
     105    public final static function get_instance() {
    178106
    179         $new_stats = apply_filters('plugin-stats_build-link', $this->fetchStats($slugname));
    180         $new_stats = true;
    181         // Put out the processed data
    182         $all_stats = get_option('plugin-stats');
    183         $plugin = $all_stats[$slugname];
    184         $processed = $plugin['processed'];
     107        // Get current instance or create a new one.
     108        if (self::$instance===null) {
     109            self::$instance = new PluginStats;
     110        }
    185111
    186         if( !$new_stats && !empty($plugin["link_$imgSize"])) {
    187             $chart = $plugin["link_$imgSize"];
    188         } else {
    189             // Trim down to what we need
    190             $size = min(count($processed['dates']), apply_filters('plugin-stats_num-days', 180));
    191             $dates = array_slice($processed['dates'], -$size);
    192             $downloads = array_slice($processed['downloads'], -$size);
    193        
    194             // Figure out where the month labels should go based on the first day of the month
    195             $monthBegins = array();
    196             $labels = array();
    197             for ($i=0; $i < $size; $i++) {
    198                 $day = $dates[$i];
    199                 if( preg_match('/\d{4}-\d{2}-01/', $day)) {
    200                     $monthBegins []= round((float)$i*100/$size,2);
    201                     $pieces = explode('-', $day);
    202                     $labels []= $wp_locale->get_month($pieces[1]);
    203                 }
    204             }
    205             // If no months have begun in the interval, stick the current month name in the middle
    206             if( empty($labels) ) {
    207                 $labels[]= $wp_locale->get_month(date('m'));   
    208                 $monthBegins[]= 50;
    209             }
    210        
    211             // Build all of the parameters for the Google Charts API
    212             $data = $this->simpleEncode($downloads, $processed['max'], $processed['min']);
    213             $chartArgs = array( 'chxr'=>"1,{$processed['min']},{$processed['max']}",
    214                 'chxp'=>'0,'.implode(',',$monthBegins), 'chxt'=>'x,y',
    215                 'chxl'=>'0:|'.implode('|', $labels), 'chs'=>$imgSize,
    216                 'cht'=>'lc', 'chd'=>$data, 'chtt'=>$slugname );
    217          
    218              if( $processed['max']!=$processed['min'] ) {
    219                  $current_ratio = round((float)((end($downloads)-$processed['min'])/($processed['max']-$processed['min'])),2);
    220                  $chartArgs['chm'] = "h,FF0000,0,$current_ratio,0.5";
     112        // Return the instance.
     113        return self::$instance;
    221114
    222                  $avgDev = $this->averageDeviation($downloads);
    223                  $begin = round(max(0,($avgDev[0]-$avgDev[1]-$processed['min'])/($processed['max']-$processed['min'])),2);
    224                  $end = round(min(1,($avgDev[0]+$avgDev[1]-$processed['min'])/($processed['max']-$processed['min'])),2);
    225                  $chartArgs['chm'] .= "|r,99FF99,0,$begin,$end";
    226              }
     115    }
    227116
    228             // Build the image link, pass it through some filters, and return it
    229             $chartArgs = apply_filters('plugin-stats_img-link-args', $chartArgs, $slugname);
    230             $chart = add_query_arg($chartArgs, 'http://chart.apis.google.com/chart?');
    231        
    232             $plugin["link_$imgSize"] = $chart;
    233             $all_stats[$slugname] = $plugin;
    234             update_option('plugin-stats', $all_stats);
    235         }
    236         return apply_filters('plugin-stats_img-link', "<img src='$chart' />", $slugname);
    237     }
    238    
    239     /**
    240      * Fetches and processes the stats for $slugname
    241      */
    242     function fetchStats($slugname) {
    243         $allStats = get_option('plugin-stats');
    244        
    245         $cacheTime = apply_filters('plugin-stats_cache-time', 600); // 10 Minute cache
    246         if( !isset($allStats[$slugname]) || time() - $allStats[$slugname]['retrieved'] > $cacheTime ) {
    247            
    248             $oldStats = $allStats[$slugname];
    249             if( empty($oldStats) ) $oldStats = array();
    250            
    251             // 1. Get the stats
    252             $stats = $this->downloadStats($slugname);
    253             if( !$stats ) return false;
    254            
    255             // 2. Parse the stats
    256             $parser = new PluginStatsParser();
    257             $processed = $parser->parse($stats);
    258 
    259             // 3. Process the stats (store a max of 180 days)
    260             $processed['dates'] = array_slice($processed['dates'], -180);
    261             $processed['downloads'] = array_slice($processed['downloads'], -180);
    262             $processed['min'] = min($processed['downloads']);
    263             $processed['max'] = max($processed['downloads']);
    264            
    265             // 4. Store the stats
    266             $newStats = array('retrieved'=>time(), 'raw'=>$stats, 'processed'=>$processed);
    267             $allStats[$slugname] = array_merge($oldStats, $newStats);
    268             update_option('plugin-stats', $allStats);
    269             return true;
    270         }
    271         return false;
    272     }
    273    
    274     /**
    275      * Downloads and returns the stats for $slugname from WP.org
    276      */
    277     function downloadStats($slugname) {
    278         require_once(ABSPATH.WPINC.'/class-snoopy.php');
    279         if( is_null($this->snoop) ) $this->snoop = new Snoopy;
    280         $this->snoop->fetch("http://wordpress.org/extend/stats/plugin-xml.php?slug=$slugname");
    281         if( $this->snoop->status == '200' ) {
    282             return $this->snoop->results;
    283         }
    284         return false;
    285     }
    286 
    287     /**
    288      * Encodes an array of values using Google Charts API Simple Encoding
    289      * From: http://groups.google.com/group/google-chart-api/browse_thread/thread/7552496ccef00d96
    290      */
    291     function simpleEncode($values, $max = 61, $min = 0){
    292         $simple_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    293         $chardata = 's:';
    294         $delta = $max - $min;
    295         $size = strlen($simple_table)-1;
    296 
    297         // prevent a divide-by-zero if $max==$min
    298         if( $delta == 0 ) return $chardata.str_repeat('A', count($values));
    299         foreach($values as $k => $v){
    300                 if($v >= $min && $v <= $max){
    301                         $chardata .= $simple_table[round($size * ($v - $min) / $delta)];
    302                 }else{
    303                         $chardata .= '_';
    304                 }
    305         }
    306         return $chardata;
    307     }
    308    
    309     /**
    310      * Calculates the average of all the elements in the array.
    311      */
    312     function average($array){
    313         if( empty($array) ) return 0;
    314         $sum   = array_sum($array);
    315         $count = count($array);
    316         return $sum/$count;
    317     }
    318 
    319     /**
    320      * Calculates the average and standard deviation of all the elements in the array.
    321      */
    322     function averageDeviation($array){
    323         $avg = $this->average($array);
    324         foreach ($array as $value) {
    325             $variance[] = pow($value-$avg, 2);
    326         }
    327         $deviation = sqrt($this->average($variance));
    328         return array($avg, $deviation);
    329     }
    330117}
    331118
    332 /**
    333  * Parser for WordPress.org plugin stats
    334  */
    335 class PluginStatsParser {
    336     var $parserState;
    337     var $parsed;
    338 
    339     function parse($xml) {
    340         $parser = xml_parser_create();
    341         xml_set_object($parser, $this);
    342         xml_set_element_handler($parser, "tag_open", "tag_close");
    343         xml_set_character_data_handler($parser, "cdata");
    344 
    345         $this->parserState = 0;
    346         $this->parsed = array('dates'=>array(), 'downloads'=>array());
    347         xml_parse($parser, trim($xml), true);
    348         xml_parser_free($parser);
    349 
    350         return $this->parsed;
    351     }
    352  
    353     /**
    354      * Tag started, modify parser state
    355      */
    356     function tag_open($parser, $tag, $attributes) {
    357         if( $tag == 'ROW' ) {
    358             $this->parserState+=3;
    359         } else if( $tag == 'STRING' ) {
    360             $this->parserState+=1;
    361         } else if( $tag == 'NUMBER' ) {
    362             $this->parserState+=2;
    363         }
    364     }
    365    
    366     /**
    367      * Got some data, save it if we're in the right state
    368      */
    369     function cdata($parser, $cdata) {
    370         if( $this->parserState == 4 ) {
    371             $this->parsed['dates'] []= $cdata;
    372         } else if( $this->parserState == 8) {
    373             $this->parsed['downloads'] []= $cdata;
    374         }
    375     }
    376 
    377     /**
    378      * Tag ended, modify parser state
    379      */
    380     function tag_close($parser, $tag) {
    381         if( $tag == 'STRING' ) {
    382             $this->parserState-=1;
    383         } else if( $tag == 'NUMBER' ) {
    384             $this->parserState-=2;
    385         }
    386     }
    387    
    388 }
    389 
    390 // Let's get this show on the road!
    391 new PluginStats();
    392 ?>
     119PluginStats::get_instance();
  • plugin-stats/trunk/readme.txt

    r1988621 r1989517  
    33Donate link: https://johnalarcon.com/
    44Tags: wordpress, admin, stats, plugin, developer, shortcode
    5 Requires at least: 2.5
    6 Tested up to: 2.5.1
    7 Stable tag: 1.1
     5Requires at least: 4.5
     6Tested up to: 5.0
     7Stable tag: trunk
    88
    99*After nearly a decade of inactivity, this plugin has been adopted! It is now being redeveloped for release soon!*
    1010
    11 *Many thanks to Edward Dale who graciously granted new ownership to make this possible! *
     11*Many thanks to Edward Dale who graciously granted new ownership to make this possible!*
Note: See TracChangeset for help on using the changeset viewer.