Plugin Directory

Changeset 3141611


Ignore:
Timestamp:
08/26/2024 10:59:14 AM (16 months ago)
Author:
veppa
Message:

Added: grouping by core function, add constant to block externals

Location:
http-requests-manager
Files:
7 edited
4 copied

Legend:

Unmodified
Added
Removed
  • http-requests-manager/tags/1.3.4/assets/css/admin.css

    r3002923 r3141611  
    9696}
    9797
    98 .media-frame-title,
    99 .media-frame-content {
     98#vphrm-modal.vphrm-modal .media-frame-title,
     99#vphrm-modal.vphrm-modal .media-frame-content {
    100100    left: 0;
    101101}
  • http-requests-manager/tags/1.3.4/assets/js/admin.js

    r3110053 r3141611  
    134134            /*var element = document.getElementById("vphrm-content");
    135135             element.scrollIntoView();*/
    136            
    137            
     136
     137
    138138
    139139            // save view
    140140            VPHRM.view_save();
    141            
    142            
     141
     142
    143143            $('select.vphrm-group-view').attr('disabled', 'disabled');
    144             setTimeout(function(){$('select.vphrm-group-view').removeAttr('disabled');},100);
     144            setTimeout(function ()
     145            {
     146                $('select.vphrm-group-view').removeAttr('disabled');
     147            }, 100);
    145148        };
    146149        VPHRM.view = function ()
     
    404407                page_style: ''
    405408            };
    406            
     409
    407410            context.page_num = context.page_num || 0;
    408411
     
    426429            // group same page by color
    427430            if (context.last_page_id !== row.page_id)
    428             {                       
     431            {
    429432                context.last_page_id = row.page_id;
    430433                context.page_num++;
    431                 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"';             
     434                context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"';
    432435            }
    433436
     
    642645                    }
    643646                }
     647
     648                // get more details about core
     649                if (row.request_source === 'core')
     650                {
     651                    var more_info = VPHRM.row_core_more_info(row,request_args);
     652                    if(more_info)
     653                    {
     654                        row.request_source += ': '+more_info;
     655                    }
     656                }
     657
    644658                // count req_types and req_sources
    645659                stats.req_types[row.request_group] = (stats.req_types[row.request_group] || 0) + 1;
     
    738752        };
    739753
     754        // get more infor about core function for grouping
     755        VPHRM.row_core_more_info = function (row, request_args)
     756        {
     757
     758            // pingback, enclosure, oembed, siteahelth, browse happy, serve happy,
     759            backtrace = request_args._info.backtrace || [];
     760            for (x in backtrace)
     761            {
     762
     763                if (backtrace[x] === 'do_enclose')
     764                {
     765                    return 'enclosure';
     766                }
     767                if (backtrace[x] === 'pingback')
     768                {
     769                    return 'pingback';
     770                }
     771                if (backtrace[x] === 'WP_oEmbed->fetch')
     772                {
     773                    return 'oEmbed';
     774                }
     775                if (backtrace[x] === '_wp_cron')
     776                {
     777                    return 'cron';
     778                }
     779                if (backtrace[x] === 'WP_Site_Health->perform_test')
     780                {
     781                    return 'health';
     782                }
     783            }
     784
     785
     786            // check url
     787            var base_url = row['url'].split('?')[0].replace('http://', 'https://');
     788       
     789            if (base_url.search('https://api.wordpress.org/plugins/update-check/') != -1)
     790            {
     791                return 'update';
     792            }
     793            if (base_url.search('https://api.wordpress.org/themes/update-check/') != -1)
     794            {
     795                return 'update';
     796            }
     797            if (base_url.search('https://api.wordpress.org/core/checksums/') != -1)
     798            {
     799                return 'update';
     800            }
     801            if (base_url.search('https://api.wordpress.org/core/version-check/') != -1)
     802            {
     803                return 'version-check';
     804            }
     805            if (base_url.search('https://api.wordpress.org/core/serve-happy/') != -1)
     806            {
     807                return 'serve-happy';
     808            }
     809            if (base_url.search('https://api.wordpress.org/core/browse-happy/') != -1)
     810            {
     811                return 'browse-happy';
     812            }
     813            if (base_url.search('https://api.wordpress.org/plugins/info/') != -1)
     814            {
     815                return 'plugins-info';
     816            }
     817            if (base_url.search('https://api.wordpress.org/translations/') != -1)
     818            {
     819                return 'translations';
     820            }
     821
     822            return '';
     823        }
     824
    740825
    741826        // Clear
     
    789874            {
    790875                page.badges = [];
     876               
    791877                page.badges.push('<i class="vphrm-badge"><b>page_type:</b> ' + page.page_type + '</i>');
    792878                page.badges.push('<i class="vphrm-badge"><b>is_user_logged_in:</b> ' + (page.info.is_user_logged_in ? 'true' : 'false') + '</i>');
     
    796882                    page.badges.push('<i class="vphrm-badge"><b>ajax_action:</b> ' + page.info.ajax_action + '</i>');
    797883                }
     884                page.badges.push('<i class="vphrm-badge"><b>requests:</b> ' + page.info.req_num + '</i>');
    798885            }
    799886
  • http-requests-manager/tags/1.3.4/http-requests-manager.php

    r3110053 r3141611  
    55  Plugin URI:   https://veppa.com/http-requests-manager/
    66  Description: Limit, Debug, Optimize WP_HTTP requests. Limit by request count, page load time, reduce timeout for each request. Speed up login and admin pages.
    7   Version: 1.3.3
     7  Version: 1.3.4
    88  Author: veppa
    99  Author URI: https://veppa.com/
     
    3232 * TODO:
    3333
    34  - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost.
    35  - when block all set and user navigates to plugins page show notification that operation mode prevents external requests.
    36  
     34  + Modal left padding 200px fixed on some sites.
     35  + Do not add cp hooks if logging disabled. waste of resources.
     36  + Group view prevented requests: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc.
     37  + Show total number of requests in details view for given page.
     38  + Force blocking rules: use double prevention in block all external and allow only wp requests by defining constants.
     39  + Make "Only log HTTP requests" default operation mode.
     40
     41  - marketing: remove all other affiliate links.
     42  -     - incorporate video tutorials to interface with popup/direct link.
     43
     44
     45  - safe-mode: show instruction about safe mode and operation mode on beginning. after dismissed move note to bottom.
     46  - [maybe] group prevent enclosure checks and pingbacks. write 1 log instead of 50+
     47  - reduce ajax data. sometimes report loads slow. 230kb compressed = 1.3mb uncompressed
     48  - truncate long response,
     49  - remove duplicate info about requests in page array.
     50  - optimization: define hooks only for selected mode and logging combination. define hooks granularly.
     51
     52  -------------------------------------------
     53 * conflict test.
     54 * 1) always monitor and log passively:
     55  - add debug log:
     56  - on shutdown check if pre_http hook and capturing hook removed. add to debug log.
     57  - check if block_external constants predefined.
     58  - keep last 20 records
     59  - use passive logging. clear log after 5 days. if issue happens and log is cleared then record.
     60  if issue happens and already in log then do not log until log cleared. 1 day, 3 day or 5 day period is good.
     61  - do this because conflict may happen on any page any time. it is not once and for all conflict.
     62 * 2) pretest before switching operation mode. try 5 pages admin and frontend then show report passed and failed.
     63 *      - can be separate button to test before switching.
     64 * 3) add safemode URL parameter to disable logging so user can switch to log only mode in case plugin conflict shows white screen of death. (otherwise user have to manually change plugin folder in order to disable blocking.)
     65  --------------------------------------------
     66
     67  - [maybe] show noticifation if constants (WP_HTTP_BLOCK_EXTERNAL, WP_ACCESSIBLE_HOSTS) defined in config (not me) and conflicting with current operation mode.
     68
     69  - when ajax called by plugin then do not block wp_HTTP by that plugin in smart block mode. example, cludflare plugin calls API 5+ times inside ajax call. updraft takes long to complete update via ajax call.
     70  - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost.
     71  - when block all set and user navigates to plugins page show notification that operation mode prevents external requests.
     72
    3773  - show 47% requests blocked in dashboard at a glance. with option to remove from there.
    3874  - delay and bulk write logs to speed up.
     
    69105{
    70106
    71     const VERSION = '1.3.3';
     107    const VERSION = '1.3.4';
    72108    const ID = 'http-requests-manager';
    73109    const TIMEOUT = 2;
     
    107143    {
    108144        self::timer_float_start();
    109         self::cp_init();
     145
     146        // cp ony if logging is not disabled
     147        if(!self::get_option('disable_logging'))
     148        {
     149            self::cp_init();
     150            add_action('shutdown', [$this, 'db_update_page']);
     151        }
    110152
    111153        // setup variables
     
    115157
    116158        add_action('init', [$this, 'init']);
    117         add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2);     
     159        add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2);
    118160        add_filter('pre_http_request', [$this, 'log_pre_http_request'], PHP_INT_MAX, 3);
    119161        add_action('http_api_debug', [$this, 'db_capture_request'], 10, 5);
    120162        add_action('vphrm_cleanup_cron', [$this, 'db_cleanup']);
    121163        add_action('pre_get_ready_cron_jobs', [$this, 'cron_prevent_in_my_ajax']);
    122         add_action('shutdown', [$this, 'db_update_page']);
    123164
    124165        // admin page actions only. for optimisation purpose these are used only on admin pages
     
    10551096    }
    10561097
    1057     function log_start_timer($parsed_args = array(),$url='')
     1098    function log_start_timer($parsed_args = array(), $url = '')
    10581099    {
    10591100        self::cp('[start] request');
     
    10661107            self::$timer_before = self::timer_float();
    10671108        }
    1068        
     1109
    10691110        // pre-populate request args [_info] for recording original url before any modification.
    10701111        // this will show url for denyed empty requests.
     
    10731114        return $parsed_args;
    10741115    }
    1075    
    1076    
     1116
    10771117    /**
    10781118     * force logging when pre populated by oyher plugin. (from cache or error)
     
    10841124     */
    10851125    function log_pre_http_request($pre, $parsed_args, $url)
    1086     {   
    1087         if(false !== $pre){
     1126    {
     1127        if(false !== $pre)
     1128        {
    10881129            // request handled by other plugin (cache or error response).
    10891130            // request will not be sent. reponse provided by other plugin. log this to debug window.
     
    11961237            }
    11971238        }
    1198        
     1239
    11991240        return self::$requests;
    12001241    }
     
    12111252        {
    12121253            case 'mode':
    1213                 $mode_default = 'block_smart';
     1254                $mode_default = 'log';
    12141255                $modes = self::modes();
    12151256                // check if mode exists
     
    13471388        return self::$modes;
    13481389    }
    1349    
    1350     static public function db_truncate_field($value,$max_length)
    1351     {
    1352         return strlen($value)>$max_length ? substr($value,0,$max_length) : $value;
    1353     } 
     1390
     1391    static public function db_truncate_field($value, $max_length)
     1392    {
     1393        return strlen($value) > $max_length ? substr($value, 0, $max_length) : $value;
     1394    }
    13541395
    13551396    function db_capture_page()
     
    13721413        $log_data = apply_filters('vphrm_log_page_data', [
    13731414            'url'        => self::page_url(),
    1374             'page_type'  => self::db_truncate_field( self::current_page_type(), 20 ),
     1415            'page_type'  => self::db_truncate_field(self::current_page_type(), 20),
    13751416            'runtime'    => self::timer_float(),
    13761417            'info'       => json_encode($info),
     
    14051446        // capture request to apply request limits even if not logging.
    14061447        $this->request_log($url, $args['stream']);
    1407        
    1408        
     1448
    14091449        // show nonempty url for checkpoint. if url empty use original url.
    1410         $url_cp = empty($url)?'[empty] '.(!empty($args['_info']['request_url_original'])?$args['_info']['request_url_original']:''):$url;
    1411        
     1450        $url_cp = empty($url) ? '[empty] ' . (!empty($args['_info']['request_url_original']) ? $args['_info']['request_url_original'] : '') : $url;
     1451
     1452        // remove request_url_original if it matches $url
     1453        if(!empty($args['_info']['request_url_original']) && $args['_info']['request_url_original'] === $url)
     1454        {
     1455            unset($args['_info']['request_url_original']);
     1456        }
     1457
     1458
    14121459        self::cp('request: ' . $url_cp);
    14131460
     
    14481495            'runtime'        => ( microtime(true) - $this->start_time ),
    14491496            'date_added'     => current_time('mysql'),
    1450             'page_id'        => self::$page_id,         
    1451             'request_status' => self::db_truncate_field( self::current_request_status($response), 20 ),
    1452             'request_group'  => self::db_truncate_field( self::current_request_group($args), 20 ),
    1453             'request_source' => self::db_truncate_field( self::current_request_source($args), 255 ),
     1497            'page_id'        => self::$page_id,
     1498            'request_status' => self::db_truncate_field(self::current_request_status($response), 20),
     1499            'request_group'  => self::db_truncate_field(self::current_request_group($args), 20),
     1500            'request_source' => self::db_truncate_field(self::current_request_source($args), 255),
    14541501        ]);
    1455                
    14561502
    14571503        if(false !== $log_data)
    14581504        {
    1459             $wpdb->insert(self::db_table_log(), $log_data);         
     1505            $wpdb->insert(self::db_table_log(), $log_data);
    14601506
    14611507            // store last request id
     
    15951641    static public function cp($title = '')
    15961642    {
    1597 
    1598         $cp_count = count(self::$cp_arr);
    1599 
    1600         if(!strlen($title))
    1601         {
    1602             $title = 'CP ' . $cp_count;
    1603         }
    1604 
    1605         $last = end(self::$cp_arr);
    1606 
    1607         $return = array('t' => self::timer_float(), 'm' => self::cp_memory());
    1608 
    1609         $return['name'] = $title;
    1610 
    1611         self::$cp_arr[] = $return;
     1643        if(!self::get_option('disable_logging'))
     1644        {
     1645            $cp_count = count(self::$cp_arr);
     1646
     1647            if(!strlen($title))
     1648            {
     1649                $title = 'CP ' . $cp_count;
     1650            }
     1651
     1652            // $last = end(self::$cp_arr);
     1653
     1654            $return = array('t' => self::timer_float(), 'm' => self::cp_memory());
     1655
     1656            $return['name'] = $title;
     1657
     1658            self::$cp_arr[] = $return;
     1659        }
    16121660    }
    16131661
     
    20622110     */
    20632111    function manage_pre_http_request($pre, $parsed_args, $url)
    2064     {   
     2112    {
    20652113        // request not handled (cache or error) by other plugins
    20662114        if(false === $pre)
    2067         {           
     2115        {
    20682116            // return error if block
    20692117            if(self::$request_action === 'block')
     
    20772125        return $pre;
    20782126    }
    2079    
    2080    
    2081     function manage_do_action_http_api_debug($pre, $parsed_args, $url){
     2127
     2128    function manage_do_action_http_api_debug($pre, $parsed_args, $url)
     2129    {
    20822130        /** This action is documented in wp-includes/class-wp-http.php */
    20832131        do_action('http_api_debug', $pre, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url);
    20842132    }
    2085    
    20862133
    20872134    /**
     
    21172164    function manage_perform_request_blocking_by_url($url, $info = '')
    21182165    {
    2119         $this->log_start_timer(array(),$url);
     2166        $this->log_start_timer(array(), $url);
    21202167
    21212168        self::$request_action = 'block';
     
    25872634        add_filter('site_transient_update_themes', [$this, 'disable_maybe_update_filter'], 10, 2);
    25882635        add_filter('site_transient_update_core', [$this, 'disable_maybe_update_filter'], 10, 2);
     2636
     2637        // double implementation for block all modes. in case some plugins remove all pre_http_request actions.
     2638        $this->manage_block_using_constants();
    25892639    }
    25902640
     
    26052655
    26062656        // default no skip
     2657        return false;
     2658    }
     2659
     2660    function manage_block_using_constants()
     2661    {
     2662        // current operation mode
     2663        $mode = self::get_mode();
     2664
     2665        $block_defined = defined('WP_HTTP_BLOCK_EXTERNAL');
     2666        $block = $block_defined ? WP_HTTP_BLOCK_EXTERNAL : false;
     2667
     2668        $host_defined = defined('WP_ACCESSIBLE_HOSTS');
     2669        $host = $host_defined ? WP_ACCESSIBLE_HOSTS : '';
     2670
     2671        //block_external
     2672        if($mode === 'block_external')
     2673        {
     2674            // block all external
     2675            if(!$block_defined && $host == '')
     2676            {
     2677                // can define our own constant here
     2678                define('WP_HTTP_BLOCK_EXTERNAL', true);
     2679                return true;
     2680            }
     2681        }
     2682
     2683        //block_external_no_wp
     2684        if($mode === 'block_external_no_wp')
     2685        {
     2686            // block all external
     2687            if(!$block_defined && (!$host_defined || $host == '*.wordpress.org'))
     2688            {
     2689                // can define our own constant here
     2690                define('WP_HTTP_BLOCK_EXTERNAL', true);
     2691
     2692                if(!$host_defined)
     2693                {
     2694                    define('WP_ACCESSIBLE_HOSTS', '*.wordpress.org');
     2695                }
     2696                return true;
     2697            }
     2698        }
     2699
    26072700        return false;
    26082701    }
     
    32913384    {
    32923385        global $wpdb;
    3293            
     3386
    32943387        $version = HTTP_Requests_Manager::VERSION;
    32953388        $db_version = HTTP_Requests_Manager::get_option('version', 0);
     
    32983391        {
    32993392            require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    3300            
     3393
    33013394            // increase request_source varchar length to 255
    33023395            // ALTER TABLE `wp_vphrm_log` CHANGE `request_source` `request_source` varchar(255);
    3303             $wpdb->query("ALTER TABLE ".self::db_table_log()." CHANGE `request_source` `request_source` varchar(255)");         
     3396            $wpdb->query("ALTER TABLE " . self::db_table_log() . " CHANGE `request_source` `request_source` varchar(255)");
    33043397        }
    33053398    }
     
    33933486VPHRM();
    33943487
    3395 
    3396 
    3397 
    3398 
  • http-requests-manager/tags/1.3.4/readme.txt

    r3110053 r3141611  
    44Tags: wp_http, log, debug, optimization, limit
    55Requires at least: 4.7
    6 Tested up to: 6.5
    7 Stable tag: 1.3.3
     6Tested up to: 6.6
     7Stable tag: 1.3.4
    88License: GPLv2
    99
     
    192192== Changelog ==
    193193
     194= 1.3.4 - 26 August 2024  =
     195
     196 * Added: Separate group view requests by core: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc.
     197 * Added: Show total number of requests in detail view for given page.
     198 * Added: When possible force blocking rules by defining constants to (block all external) and (allow only wp requests).
     199 * Fixed: Do not add cp (checkpoint) hooks if logging disabled. Prevent waste of memory.
     200 * Update: Made "Only log HTTP requests" default operation mode.
     201
    194202= 1.3.3 - 30 June 2024  =
    195203 
    196  * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL whill be shows inside Checkpoint. 
     204 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL will be shows inside Checkpoint. 
    197205 * Added: When request responded (from cache or error) by other plugin without sending to remote server it will be labeled as 'other' and not blocked.
    198206 * Fixed: color coding requests from same page on logs page.
    199  * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now databse field length increased and longer strings will be truncated to fit when needed.
     207 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now database field length increased and longer strings will be truncated to fit when needed.
    200208 
    201209= 1.3.2 - 26 June 2024  =
  • http-requests-manager/tags/1.3.4/templates/page-settings.php

    r3081974 r3141611  
    182182                    <li><?php _e('Maximum 10 custom rules allowed. Please contact if you need more.', 'http-requests-manager'); ?></li>
    183183                </ol>
    184 
    185                 <p><b><a href="https://veppa.com/http-requests-manager/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Learn more', 'http-requests-manager') ?> →</a></b></p>
     184               
     185                <p><b><a href="https://veppa.com/allow-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Allow Request Tutorial', 'http-requests-manager') ?> →</a></b></p>
     186                <p><b><a href="https://veppa.com/block-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Block Request Tutorial', 'http-requests-manager') ?> →</a></b></p>
    186187
    187188            </div>
     
    225226            <!-- more  -->
    226227            <p>
    227                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/" target="_blank">
     228                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank">
    228229                    <span class="vphrm-card-val">⦿</span>
    229230                    <span class="vphrm-card-name"><b class="vphrm-card-h3">Share button without plugin</b>
    230231                        Fast load times, tiny code, no negative effect on Page Speed score. Free.</span>
    231232                </a>
    232                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#rankmath" target="_blank">
     233                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/speed/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank">
     234                    <span class="vphrm-card-val">⪫</span>
     235                    <span class="vphrm-card-name"><b class="vphrm-card-h3">90+ PageSpeed</b>
     236                    Download PDF checklist that I use to get 90+ score on WordPress websites.</span>
     237                </a>
     238                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#rankmath" target="_blank">
    233239                    <span class="vphrm-card-val">◢</span>
    234240                    <span class="vphrm-card-name"><b class="vphrm-card-h3">Rank Math SEO plugin</b>
    235241                        Optimize posts for target keyword. Content SEO checklist with best practices. Track SEO performance for each post using Google Search Console data.</span>
    236242                </a>
    237                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#a2hosting" target="_blank">
     243                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#a2hosting" target="_blank">
    238244                    <span class="vphrm-card-val">a2</span>
    239245                    <span class="vphrm-card-name"><b class="vphrm-card-h3">A2hosting</b>
    240246                        WordPress hosting for any website. Good for beginners and has managed hosting for big sites as well.</span>
    241247                </a>
    242                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#kwfinder" target="_blank">
     248                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#kwfinder" target="_blank">
    243249                    <span class="vphrm-card-val">𝕂</span>
    244250                    <span class="vphrm-card-name"><b class="vphrm-card-h3">KWfinder</b>
     
    264270                        <span class="vphrm-card-name"><?php _e('Support forum', 'http-requests-manager'); ?></span>
    265271                    </a>
     272                    <a class="vphrm-card vphrm-card-wide" href="https://youtube.com/playlist?list=PLvn-qBzU0II7b5D4OYDnKpNpuvxiM0f4b" target="_blank">
     273                        <span class="vphrm-card-val">►</span>
     274                        <span class="vphrm-card-name"><?php _e('Video tutorials', 'http-requests-manager'); ?></span>
     275                    </a>
    266276                </p>
    267277
     
    283293                    </a>
    284294                </p>
     295               
     296                <p><b><?php _e('Thank you!', 'http-requests-manager') ?></b></p>
    285297
    286298                <p><a href="#top"><?php _e('Back to top', 'http-requests-manager') ?> ↑</a></p>     
  • http-requests-manager/trunk/assets/css/admin.css

    r3002923 r3141611  
    9696}
    9797
    98 .media-frame-title,
    99 .media-frame-content {
     98#vphrm-modal.vphrm-modal .media-frame-title,
     99#vphrm-modal.vphrm-modal .media-frame-content {
    100100    left: 0;
    101101}
  • http-requests-manager/trunk/assets/js/admin.js

    r3110053 r3141611  
    134134            /*var element = document.getElementById("vphrm-content");
    135135             element.scrollIntoView();*/
    136            
    137            
     136
     137
    138138
    139139            // save view
    140140            VPHRM.view_save();
    141            
    142            
     141
     142
    143143            $('select.vphrm-group-view').attr('disabled', 'disabled');
    144             setTimeout(function(){$('select.vphrm-group-view').removeAttr('disabled');},100);
     144            setTimeout(function ()
     145            {
     146                $('select.vphrm-group-view').removeAttr('disabled');
     147            }, 100);
    145148        };
    146149        VPHRM.view = function ()
     
    404407                page_style: ''
    405408            };
    406            
     409
    407410            context.page_num = context.page_num || 0;
    408411
     
    426429            // group same page by color
    427430            if (context.last_page_id !== row.page_id)
    428             {                       
     431            {
    429432                context.last_page_id = row.page_id;
    430433                context.page_num++;
    431                 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"';             
     434                context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"';
    432435            }
    433436
     
    642645                    }
    643646                }
     647
     648                // get more details about core
     649                if (row.request_source === 'core')
     650                {
     651                    var more_info = VPHRM.row_core_more_info(row,request_args);
     652                    if(more_info)
     653                    {
     654                        row.request_source += ': '+more_info;
     655                    }
     656                }
     657
    644658                // count req_types and req_sources
    645659                stats.req_types[row.request_group] = (stats.req_types[row.request_group] || 0) + 1;
     
    738752        };
    739753
     754        // get more infor about core function for grouping
     755        VPHRM.row_core_more_info = function (row, request_args)
     756        {
     757
     758            // pingback, enclosure, oembed, siteahelth, browse happy, serve happy,
     759            backtrace = request_args._info.backtrace || [];
     760            for (x in backtrace)
     761            {
     762
     763                if (backtrace[x] === 'do_enclose')
     764                {
     765                    return 'enclosure';
     766                }
     767                if (backtrace[x] === 'pingback')
     768                {
     769                    return 'pingback';
     770                }
     771                if (backtrace[x] === 'WP_oEmbed->fetch')
     772                {
     773                    return 'oEmbed';
     774                }
     775                if (backtrace[x] === '_wp_cron')
     776                {
     777                    return 'cron';
     778                }
     779                if (backtrace[x] === 'WP_Site_Health->perform_test')
     780                {
     781                    return 'health';
     782                }
     783            }
     784
     785
     786            // check url
     787            var base_url = row['url'].split('?')[0].replace('http://', 'https://');
     788       
     789            if (base_url.search('https://api.wordpress.org/plugins/update-check/') != -1)
     790            {
     791                return 'update';
     792            }
     793            if (base_url.search('https://api.wordpress.org/themes/update-check/') != -1)
     794            {
     795                return 'update';
     796            }
     797            if (base_url.search('https://api.wordpress.org/core/checksums/') != -1)
     798            {
     799                return 'update';
     800            }
     801            if (base_url.search('https://api.wordpress.org/core/version-check/') != -1)
     802            {
     803                return 'version-check';
     804            }
     805            if (base_url.search('https://api.wordpress.org/core/serve-happy/') != -1)
     806            {
     807                return 'serve-happy';
     808            }
     809            if (base_url.search('https://api.wordpress.org/core/browse-happy/') != -1)
     810            {
     811                return 'browse-happy';
     812            }
     813            if (base_url.search('https://api.wordpress.org/plugins/info/') != -1)
     814            {
     815                return 'plugins-info';
     816            }
     817            if (base_url.search('https://api.wordpress.org/translations/') != -1)
     818            {
     819                return 'translations';
     820            }
     821
     822            return '';
     823        }
     824
    740825
    741826        // Clear
     
    789874            {
    790875                page.badges = [];
     876               
    791877                page.badges.push('<i class="vphrm-badge"><b>page_type:</b> ' + page.page_type + '</i>');
    792878                page.badges.push('<i class="vphrm-badge"><b>is_user_logged_in:</b> ' + (page.info.is_user_logged_in ? 'true' : 'false') + '</i>');
     
    796882                    page.badges.push('<i class="vphrm-badge"><b>ajax_action:</b> ' + page.info.ajax_action + '</i>');
    797883                }
     884                page.badges.push('<i class="vphrm-badge"><b>requests:</b> ' + page.info.req_num + '</i>');
    798885            }
    799886
  • http-requests-manager/trunk/http-requests-manager.php

    r3110053 r3141611  
    55  Plugin URI:   https://veppa.com/http-requests-manager/
    66  Description: Limit, Debug, Optimize WP_HTTP requests. Limit by request count, page load time, reduce timeout for each request. Speed up login and admin pages.
    7   Version: 1.3.3
     7  Version: 1.3.4
    88  Author: veppa
    99  Author URI: https://veppa.com/
     
    3232 * TODO:
    3333
    34  - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost.
    35  - when block all set and user navigates to plugins page show notification that operation mode prevents external requests.
    36  
     34  + Modal left padding 200px fixed on some sites.
     35  + Do not add cp hooks if logging disabled. waste of resources.
     36  + Group view prevented requests: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc.
     37  + Show total number of requests in details view for given page.
     38  + Force blocking rules: use double prevention in block all external and allow only wp requests by defining constants.
     39  + Make "Only log HTTP requests" default operation mode.
     40
     41  - marketing: remove all other affiliate links.
     42  -     - incorporate video tutorials to interface with popup/direct link.
     43
     44
     45  - safe-mode: show instruction about safe mode and operation mode on beginning. after dismissed move note to bottom.
     46  - [maybe] group prevent enclosure checks and pingbacks. write 1 log instead of 50+
     47  - reduce ajax data. sometimes report loads slow. 230kb compressed = 1.3mb uncompressed
     48  - truncate long response,
     49  - remove duplicate info about requests in page array.
     50  - optimization: define hooks only for selected mode and logging combination. define hooks granularly.
     51
     52  -------------------------------------------
     53 * conflict test.
     54 * 1) always monitor and log passively:
     55  - add debug log:
     56  - on shutdown check if pre_http hook and capturing hook removed. add to debug log.
     57  - check if block_external constants predefined.
     58  - keep last 20 records
     59  - use passive logging. clear log after 5 days. if issue happens and log is cleared then record.
     60  if issue happens and already in log then do not log until log cleared. 1 day, 3 day or 5 day period is good.
     61  - do this because conflict may happen on any page any time. it is not once and for all conflict.
     62 * 2) pretest before switching operation mode. try 5 pages admin and frontend then show report passed and failed.
     63 *      - can be separate button to test before switching.
     64 * 3) add safemode URL parameter to disable logging so user can switch to log only mode in case plugin conflict shows white screen of death. (otherwise user have to manually change plugin folder in order to disable blocking.)
     65  --------------------------------------------
     66
     67  - [maybe] show noticifation if constants (WP_HTTP_BLOCK_EXTERNAL, WP_ACCESSIBLE_HOSTS) defined in config (not me) and conflicting with current operation mode.
     68
     69  - when ajax called by plugin then do not block wp_HTTP by that plugin in smart block mode. example, cludflare plugin calls API 5+ times inside ajax call. updraft takes long to complete update via ajax call.
     70  - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost.
     71  - when block all set and user navigates to plugins page show notification that operation mode prevents external requests.
     72
    3773  - show 47% requests blocked in dashboard at a glance. with option to remove from there.
    3874  - delay and bulk write logs to speed up.
     
    69105{
    70106
    71     const VERSION = '1.3.3';
     107    const VERSION = '1.3.4';
    72108    const ID = 'http-requests-manager';
    73109    const TIMEOUT = 2;
     
    107143    {
    108144        self::timer_float_start();
    109         self::cp_init();
     145
     146        // cp ony if logging is not disabled
     147        if(!self::get_option('disable_logging'))
     148        {
     149            self::cp_init();
     150            add_action('shutdown', [$this, 'db_update_page']);
     151        }
    110152
    111153        // setup variables
     
    115157
    116158        add_action('init', [$this, 'init']);
    117         add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2);     
     159        add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2);
    118160        add_filter('pre_http_request', [$this, 'log_pre_http_request'], PHP_INT_MAX, 3);
    119161        add_action('http_api_debug', [$this, 'db_capture_request'], 10, 5);
    120162        add_action('vphrm_cleanup_cron', [$this, 'db_cleanup']);
    121163        add_action('pre_get_ready_cron_jobs', [$this, 'cron_prevent_in_my_ajax']);
    122         add_action('shutdown', [$this, 'db_update_page']);
    123164
    124165        // admin page actions only. for optimisation purpose these are used only on admin pages
     
    10551096    }
    10561097
    1057     function log_start_timer($parsed_args = array(),$url='')
     1098    function log_start_timer($parsed_args = array(), $url = '')
    10581099    {
    10591100        self::cp('[start] request');
     
    10661107            self::$timer_before = self::timer_float();
    10671108        }
    1068        
     1109
    10691110        // pre-populate request args [_info] for recording original url before any modification.
    10701111        // this will show url for denyed empty requests.
     
    10731114        return $parsed_args;
    10741115    }
    1075    
    1076    
     1116
    10771117    /**
    10781118     * force logging when pre populated by oyher plugin. (from cache or error)
     
    10841124     */
    10851125    function log_pre_http_request($pre, $parsed_args, $url)
    1086     {   
    1087         if(false !== $pre){
     1126    {
     1127        if(false !== $pre)
     1128        {
    10881129            // request handled by other plugin (cache or error response).
    10891130            // request will not be sent. reponse provided by other plugin. log this to debug window.
     
    11961237            }
    11971238        }
    1198        
     1239
    11991240        return self::$requests;
    12001241    }
     
    12111252        {
    12121253            case 'mode':
    1213                 $mode_default = 'block_smart';
     1254                $mode_default = 'log';
    12141255                $modes = self::modes();
    12151256                // check if mode exists
     
    13471388        return self::$modes;
    13481389    }
    1349    
    1350     static public function db_truncate_field($value,$max_length)
    1351     {
    1352         return strlen($value)>$max_length ? substr($value,0,$max_length) : $value;
    1353     } 
     1390
     1391    static public function db_truncate_field($value, $max_length)
     1392    {
     1393        return strlen($value) > $max_length ? substr($value, 0, $max_length) : $value;
     1394    }
    13541395
    13551396    function db_capture_page()
     
    13721413        $log_data = apply_filters('vphrm_log_page_data', [
    13731414            'url'        => self::page_url(),
    1374             'page_type'  => self::db_truncate_field( self::current_page_type(), 20 ),
     1415            'page_type'  => self::db_truncate_field(self::current_page_type(), 20),
    13751416            'runtime'    => self::timer_float(),
    13761417            'info'       => json_encode($info),
     
    14051446        // capture request to apply request limits even if not logging.
    14061447        $this->request_log($url, $args['stream']);
    1407        
    1408        
     1448
    14091449        // show nonempty url for checkpoint. if url empty use original url.
    1410         $url_cp = empty($url)?'[empty] '.(!empty($args['_info']['request_url_original'])?$args['_info']['request_url_original']:''):$url;
    1411        
     1450        $url_cp = empty($url) ? '[empty] ' . (!empty($args['_info']['request_url_original']) ? $args['_info']['request_url_original'] : '') : $url;
     1451
     1452        // remove request_url_original if it matches $url
     1453        if(!empty($args['_info']['request_url_original']) && $args['_info']['request_url_original'] === $url)
     1454        {
     1455            unset($args['_info']['request_url_original']);
     1456        }
     1457
     1458
    14121459        self::cp('request: ' . $url_cp);
    14131460
     
    14481495            'runtime'        => ( microtime(true) - $this->start_time ),
    14491496            'date_added'     => current_time('mysql'),
    1450             'page_id'        => self::$page_id,         
    1451             'request_status' => self::db_truncate_field( self::current_request_status($response), 20 ),
    1452             'request_group'  => self::db_truncate_field( self::current_request_group($args), 20 ),
    1453             'request_source' => self::db_truncate_field( self::current_request_source($args), 255 ),
     1497            'page_id'        => self::$page_id,
     1498            'request_status' => self::db_truncate_field(self::current_request_status($response), 20),
     1499            'request_group'  => self::db_truncate_field(self::current_request_group($args), 20),
     1500            'request_source' => self::db_truncate_field(self::current_request_source($args), 255),
    14541501        ]);
    1455                
    14561502
    14571503        if(false !== $log_data)
    14581504        {
    1459             $wpdb->insert(self::db_table_log(), $log_data);         
     1505            $wpdb->insert(self::db_table_log(), $log_data);
    14601506
    14611507            // store last request id
     
    15951641    static public function cp($title = '')
    15961642    {
    1597 
    1598         $cp_count = count(self::$cp_arr);
    1599 
    1600         if(!strlen($title))
    1601         {
    1602             $title = 'CP ' . $cp_count;
    1603         }
    1604 
    1605         $last = end(self::$cp_arr);
    1606 
    1607         $return = array('t' => self::timer_float(), 'm' => self::cp_memory());
    1608 
    1609         $return['name'] = $title;
    1610 
    1611         self::$cp_arr[] = $return;
     1643        if(!self::get_option('disable_logging'))
     1644        {
     1645            $cp_count = count(self::$cp_arr);
     1646
     1647            if(!strlen($title))
     1648            {
     1649                $title = 'CP ' . $cp_count;
     1650            }
     1651
     1652            // $last = end(self::$cp_arr);
     1653
     1654            $return = array('t' => self::timer_float(), 'm' => self::cp_memory());
     1655
     1656            $return['name'] = $title;
     1657
     1658            self::$cp_arr[] = $return;
     1659        }
    16121660    }
    16131661
     
    20622110     */
    20632111    function manage_pre_http_request($pre, $parsed_args, $url)
    2064     {   
     2112    {
    20652113        // request not handled (cache or error) by other plugins
    20662114        if(false === $pre)
    2067         {           
     2115        {
    20682116            // return error if block
    20692117            if(self::$request_action === 'block')
     
    20772125        return $pre;
    20782126    }
    2079    
    2080    
    2081     function manage_do_action_http_api_debug($pre, $parsed_args, $url){
     2127
     2128    function manage_do_action_http_api_debug($pre, $parsed_args, $url)
     2129    {
    20822130        /** This action is documented in wp-includes/class-wp-http.php */
    20832131        do_action('http_api_debug', $pre, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url);
    20842132    }
    2085    
    20862133
    20872134    /**
     
    21172164    function manage_perform_request_blocking_by_url($url, $info = '')
    21182165    {
    2119         $this->log_start_timer(array(),$url);
     2166        $this->log_start_timer(array(), $url);
    21202167
    21212168        self::$request_action = 'block';
     
    25872634        add_filter('site_transient_update_themes', [$this, 'disable_maybe_update_filter'], 10, 2);
    25882635        add_filter('site_transient_update_core', [$this, 'disable_maybe_update_filter'], 10, 2);
     2636
     2637        // double implementation for block all modes. in case some plugins remove all pre_http_request actions.
     2638        $this->manage_block_using_constants();
    25892639    }
    25902640
     
    26052655
    26062656        // default no skip
     2657        return false;
     2658    }
     2659
     2660    function manage_block_using_constants()
     2661    {
     2662        // current operation mode
     2663        $mode = self::get_mode();
     2664
     2665        $block_defined = defined('WP_HTTP_BLOCK_EXTERNAL');
     2666        $block = $block_defined ? WP_HTTP_BLOCK_EXTERNAL : false;
     2667
     2668        $host_defined = defined('WP_ACCESSIBLE_HOSTS');
     2669        $host = $host_defined ? WP_ACCESSIBLE_HOSTS : '';
     2670
     2671        //block_external
     2672        if($mode === 'block_external')
     2673        {
     2674            // block all external
     2675            if(!$block_defined && $host == '')
     2676            {
     2677                // can define our own constant here
     2678                define('WP_HTTP_BLOCK_EXTERNAL', true);
     2679                return true;
     2680            }
     2681        }
     2682
     2683        //block_external_no_wp
     2684        if($mode === 'block_external_no_wp')
     2685        {
     2686            // block all external
     2687            if(!$block_defined && (!$host_defined || $host == '*.wordpress.org'))
     2688            {
     2689                // can define our own constant here
     2690                define('WP_HTTP_BLOCK_EXTERNAL', true);
     2691
     2692                if(!$host_defined)
     2693                {
     2694                    define('WP_ACCESSIBLE_HOSTS', '*.wordpress.org');
     2695                }
     2696                return true;
     2697            }
     2698        }
     2699
    26072700        return false;
    26082701    }
     
    32913384    {
    32923385        global $wpdb;
    3293            
     3386
    32943387        $version = HTTP_Requests_Manager::VERSION;
    32953388        $db_version = HTTP_Requests_Manager::get_option('version', 0);
     
    32983391        {
    32993392            require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    3300            
     3393
    33013394            // increase request_source varchar length to 255
    33023395            // ALTER TABLE `wp_vphrm_log` CHANGE `request_source` `request_source` varchar(255);
    3303             $wpdb->query("ALTER TABLE ".self::db_table_log()." CHANGE `request_source` `request_source` varchar(255)");         
     3396            $wpdb->query("ALTER TABLE " . self::db_table_log() . " CHANGE `request_source` `request_source` varchar(255)");
    33043397        }
    33053398    }
     
    33933486VPHRM();
    33943487
    3395 
    3396 
    3397 
    3398 
  • http-requests-manager/trunk/readme.txt

    r3110053 r3141611  
    44Tags: wp_http, log, debug, optimization, limit
    55Requires at least: 4.7
    6 Tested up to: 6.5
    7 Stable tag: 1.3.3
     6Tested up to: 6.6
     7Stable tag: 1.3.4
    88License: GPLv2
    99
     
    192192== Changelog ==
    193193
     194= 1.3.4 - 26 August 2024  =
     195
     196 * Added: Separate group view requests by core: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc.
     197 * Added: Show total number of requests in detail view for given page.
     198 * Added: When possible force blocking rules by defining constants to (block all external) and (allow only wp requests).
     199 * Fixed: Do not add cp (checkpoint) hooks if logging disabled. Prevent waste of memory.
     200 * Update: Made "Only log HTTP requests" default operation mode.
     201
    194202= 1.3.3 - 30 June 2024  =
    195203 
    196  * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL whill be shows inside Checkpoint. 
     204 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL will be shows inside Checkpoint. 
    197205 * Added: When request responded (from cache or error) by other plugin without sending to remote server it will be labeled as 'other' and not blocked.
    198206 * Fixed: color coding requests from same page on logs page.
    199  * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now databse field length increased and longer strings will be truncated to fit when needed.
     207 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now database field length increased and longer strings will be truncated to fit when needed.
    200208 
    201209= 1.3.2 - 26 June 2024  =
  • http-requests-manager/trunk/templates/page-settings.php

    r3081974 r3141611  
    182182                    <li><?php _e('Maximum 10 custom rules allowed. Please contact if you need more.', 'http-requests-manager'); ?></li>
    183183                </ol>
    184 
    185                 <p><b><a href="https://veppa.com/http-requests-manager/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Learn more', 'http-requests-manager') ?> →</a></b></p>
     184               
     185                <p><b><a href="https://veppa.com/allow-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Allow Request Tutorial', 'http-requests-manager') ?> →</a></b></p>
     186                <p><b><a href="https://veppa.com/block-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Block Request Tutorial', 'http-requests-manager') ?> →</a></b></p>
    186187
    187188            </div>
     
    225226            <!-- more  -->
    226227            <p>
    227                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/" target="_blank">
     228                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank">
    228229                    <span class="vphrm-card-val">⦿</span>
    229230                    <span class="vphrm-card-name"><b class="vphrm-card-h3">Share button without plugin</b>
    230231                        Fast load times, tiny code, no negative effect on Page Speed score. Free.</span>
    231232                </a>
    232                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#rankmath" target="_blank">
     233                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/speed/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank">
     234                    <span class="vphrm-card-val">⪫</span>
     235                    <span class="vphrm-card-name"><b class="vphrm-card-h3">90+ PageSpeed</b>
     236                    Download PDF checklist that I use to get 90+ score on WordPress websites.</span>
     237                </a>
     238                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#rankmath" target="_blank">
    233239                    <span class="vphrm-card-val">◢</span>
    234240                    <span class="vphrm-card-name"><b class="vphrm-card-h3">Rank Math SEO plugin</b>
    235241                        Optimize posts for target keyword. Content SEO checklist with best practices. Track SEO performance for each post using Google Search Console data.</span>
    236242                </a>
    237                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#a2hosting" target="_blank">
     243                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#a2hosting" target="_blank">
    238244                    <span class="vphrm-card-val">a2</span>
    239245                    <span class="vphrm-card-name"><b class="vphrm-card-h3">A2hosting</b>
    240246                        WordPress hosting for any website. Good for beginners and has managed hosting for big sites as well.</span>
    241247                </a>
    242                 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#kwfinder" target="_blank">
     248                <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#kwfinder" target="_blank">
    243249                    <span class="vphrm-card-val">𝕂</span>
    244250                    <span class="vphrm-card-name"><b class="vphrm-card-h3">KWfinder</b>
     
    264270                        <span class="vphrm-card-name"><?php _e('Support forum', 'http-requests-manager'); ?></span>
    265271                    </a>
     272                    <a class="vphrm-card vphrm-card-wide" href="https://youtube.com/playlist?list=PLvn-qBzU0II7b5D4OYDnKpNpuvxiM0f4b" target="_blank">
     273                        <span class="vphrm-card-val">►</span>
     274                        <span class="vphrm-card-name"><?php _e('Video tutorials', 'http-requests-manager'); ?></span>
     275                    </a>
    266276                </p>
    267277
     
    283293                    </a>
    284294                </p>
     295               
     296                <p><b><?php _e('Thank you!', 'http-requests-manager') ?></b></p>
    285297
    286298                <p><a href="#top"><?php _e('Back to top', 'http-requests-manager') ?> ↑</a></p>     
Note: See TracChangeset for help on using the changeset viewer.