2017-07-13 19:48:57 +00:00
< ? php
register_module ([
" name " => " Statistics " ,
2017-09-16 16:31:30 +00:00
" version " => " 0.2 " ,
2017-07-13 19:48:57 +00:00
" author " => " Starbeamrainbowlabs " ,
" description " => " An extensible statistics calculation system. Comes with a range of built-in statistics, but can be extended by other modules too. " ,
" id " => " feature-stats " ,
" code " => function () {
2017-09-16 16:31:30 +00:00
global $settings , $env ;
2017-07-13 19:48:57 +00:00
/**
2017-09-16 16:31:30 +00:00
* @ api { get } ? action = stats Show wiki statistics
* @ apiName Stats
2017-07-14 20:28:28 +00:00
* @ apiGroup Utility
2017-09-16 16:31:30 +00:00
* @ apiPermission Anonymous
* @ since v0 . 15
* @ apiParam { string } format Specify the format the data should be returned in . Supported formats : html ( default ), json .
* @ apiParam { string } stat HTML format only . If specified the page for the stat with this id is sent instead of the list of scalar stats .
2017-07-13 19:48:57 +00:00
*/
/*
2017-07-14 21:13:13 +00:00
* ███████ ████████ █████ ████████ ███████
* ██ ██ ██ ██ ██ ██
* ███████ ██ ███████ ██ ███████
* ██ ██ ██ ██ ██ ██
* ███████ ██ ██ ██ ██ ███████
*/
add_action ( " stats " , function () {
global $settings , $statistic_calculators ;
2017-09-16 16:31:30 +00:00
$allowed_formats = [ " html " , " json " ];
$format = $_GET [ " format " ] ? ? " html " ;
if ( ! in_array ( $format , $allowed_formats )) {
http_response_code ( 400 );
exit ( page_renderer :: render_main ( " Format error - $settings->sitename " , " <p>Error: The format ' $format ' is not currently supported by $settings->sitename . Supported formats: " . implode ( " , " , $allowed_formats ) . " . " ));
}
2017-07-14 21:13:13 +00:00
$stats = stats_load ();
2017-09-16 16:31:30 +00:00
if ( $format == " json " ) {
header ( " content-type: application/json " );
exit ( json_encode ( $stats , JSON_PRETTY_PRINT ));
}
2017-09-16 12:26:12 +00:00
$stat_pages_list = " <a href='?action=stats'>Main</a> | " ;
2017-07-14 21:13:13 +00:00
foreach ( $statistic_calculators as $stat_id => $stat_calculator ) {
2017-09-16 12:26:12 +00:00
if ( $stat_calculator [ " type " ] == " scalar " )
continue ;
$stat_pages_list .= " <a href='?action=stats&stat= " . rawurlencode ( $stat_id ) . " '> { $stat_calculator [ " name " ] } </a> | " ;
2017-07-14 21:13:13 +00:00
}
2017-09-16 12:26:12 +00:00
$stat_pages_list = trim ( $stat_pages_list , " | " );
2017-07-14 21:13:13 +00:00
2017-09-16 12:26:12 +00:00
if ( ! empty ( $_GET [ " stat " ]) && ! empty ( $statistic_calculators [ $_GET [ " stat " ]])) {
$stat_calculator = $statistic_calculators [ $_GET [ " stat " ]];
$content = " <h1> { $stat_calculator [ " name " ] } - Statistics</h1> \n " ;
$content .= " <p> $stat_pages_list </p> \n " ;
2017-09-16 16:31:30 +00:00
switch ( $stat_calculator [ " type " ]) {
case " page-list " :
if ( ! module_exists ( " page-list " )) {
$content .= " <p> $settings->sitename doesn't current have the page listing module installed, so HTML rendering of this statistic is currently unavailable. Try <a href='mailto: " . hide_email ( $settings -> admindetails_email ) . " '>contacting $settings->admindetails_name </a>, $settings->sitename 's administrator and asking then to install the <code>page-list</code> module.</p> " ;
break ;
}
$content .= generate_page_list ( $stats -> { $_GET [ " stat " ]} -> value );
break ;
case " page " :
$content .= $stat_calculator [ " render " ]( $stats -> { $_GET [ " stat " ]});
break ;
}
2017-09-16 12:26:12 +00:00
}
else
{
$content = " <h1>Statistics</h1> \n " ;
2017-09-16 16:31:30 +00:00
$content .= " <p>This page contains a selection of statistics about $settings->sitename 's content. They are updated automatically about every " . trim ( str_replace ([ " ago " , " 1 " ], [ " " ], human_time ( $settings -> stats_update_interval ))) . " , although $settings->sitename 's local friendly moderators may update them earlier (you can see their names at the bottom of every page).</p> \n " ;
2017-09-16 12:26:12 +00:00
$content .= " <p> $stat_pages_list </p> \n " ;
$content .= " <table class='stats-table'> \n " ;
$content .= " \t <tr><th>Statistic</th><th>Value</th></tr> \n \n " ;
foreach ( $statistic_calculators as $stat_id => $stat_calculator ) {
if ( $stat_calculator [ " type " ] !== " scalar " )
continue ;
$content .= " \t <tr><td> { $stat_calculator [ " name " ] } </td><td> { $stats -> $stat_id -> value } </td></tr> \n " ;
}
$content .= " </table> \n " ;
2017-07-14 21:13:13 +00:00
}
exit ( page_renderer :: render_main ( " Statistics - $settings->sitename " , $content ));
});
2017-09-16 16:31:30 +00:00
/**
* @ api { get | post } ? action = stats - update Recalculate the wiki ' s statistics
* @ apiName UpdateStats
* @ apiGroup Utility
* @ apiPermission Administrator
* @ since v0 . 15
* @ apiParam { string } secret POST only , optional . If you 're not logged in, you can specify the wiki' s sekret instead ( find it in peppermint . json ) using this parameter .
* @ apiParam { bool } force Whether the statistics should be recalculated anyway - even if they have already recently been recalculated . Default : no . Supported values : yes , no .
*/
2017-07-14 20:28:28 +00:00
2017-07-14 21:13:13 +00:00
/*
* ███████ ████████ █████ ████████ ███████
* ██ ██ ██ ██ ██ ██
* ███████ ██ ███████ ██ ███████
* ██ ██ ██ ██ ██ ██
* ███████ ██ ██ ██ ██ ███████
*
* ██ ██ ██████ ██████ █████ ████████ ███████
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██ ██ ██████ ██ ██ ███████ ██ █████
* ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██████ ██ ██████ ██ ██ ██ ███████
2017-07-13 19:48:57 +00:00
*/
add_action ( " stats-update " , function () {
2017-07-13 21:12:12 +00:00
global $env , $paths , $settings ;
2017-07-14 19:35:55 +00:00
if ( ! $env -> is_admin &&
(
empty ( $_POST [ " secret " ]) ||
$_POST [ " secret " ] !== $settings -> secret
)
)
2017-07-13 21:12:12 +00:00
exit ( page_renderer :: render_main ( " Error - Recalculating Statistics - $settings->sitename " , " <p>You need to be logged in as a moderator or better to get $settings->sitename to recalculate it's statistics. If you're logged in, try <a href='?action=logout'>logging out</a> and logging in again as a moderator. If you aren't logged in, try <a href='?action=login&returnto=%3Faction%3Dstats-update'>logging in</a>.</p> " ));
2017-07-13 19:48:57 +00:00
2017-07-14 20:22:37 +00:00
// Delete the old stats cache
unlink ( $paths -> statsindex );
2017-09-16 16:31:30 +00:00
update_statistics ( true , ( $_GET [ " force " ] ? ? " no " ) == " yes " );
2017-07-13 19:48:57 +00:00
header ( " content-type: application/json " );
echo ( file_get_contents ( $paths -> statsindex ) . " \n " );
});
add_help_section ( " 150-statistics " , " Statistics " , " <p></p> " );
//////////////////////////
/// Built-in Statisics ///
//////////////////////////
// The longest pages
statistic_add ([
" id " => " longest-pages " ,
" name " => " Longest Pages " ,
2017-09-16 12:26:12 +00:00
" type " => " page " ,
2017-07-13 19:48:57 +00:00
" update " => function ( $old_stats ) {
global $pageindex ;
$result = new stdClass (); // completed, value, state
$pages = [];
foreach ( $pageindex as $pagename => $pagedata ) {
$pages [ $pagename ] = $pagedata -> size ;
}
arsort ( $pages );
$result -> value = $pages ;
$result -> completed = true ;
return $result ;
2017-07-14 21:13:13 +00:00
},
" render " => function ( $stats_data ) {
$result = " <h2> $stats_data->name </h2> \n " ;
$result .= " <ol class='stats-list longest-pages-list'> \n " ;
$i = 0 ;
foreach ( $stats_data -> value as $pagename => $page_length ) {
$result .= " \t <li class='stats-item long-page'> $pagename <em>( " . human_filesize ( $page_length ) . " )</em></li> \n " ;
$i ++ ;
}
$result .= " </ol> \n " ;
return $result ;
2017-07-13 19:48:57 +00:00
}
]);
statistic_add ([
" id " => " page_count " ,
" name " => " Page Count " ,
2017-09-16 12:26:12 +00:00
" type " => " scalar " ,
2017-07-13 19:48:57 +00:00
" update " => function ( $old_stats ) {
global $pageindex ;
$result = new stdClass (); // completed, value, state
$result -> completed = true ;
$result -> value = count ( get_object_vars ( $pageindex ));
return $result ;
}
]);
statistic_add ([
" id " => " file_count " ,
" name " => " File Count " ,
2017-09-16 12:26:12 +00:00
" type " => " scalar " ,
2017-07-13 19:48:57 +00:00
" update " => function ( $old_stats ) {
global $pageindex ;
$result = new stdClass (); // completed, value, state
$result -> completed = true ;
$result -> value = 0 ;
foreach ( $pageindex as $pagename => $pagedata ) {
if ( ! empty ( $pagedata -> uploadedfile ) && $pagedata -> uploadedfile )
$result -> value ++ ;
}
return $result ;
}
]);
2017-08-14 17:48:51 +00:00
// Perform an automatic recalculation of the statistics if needed
2017-09-16 16:31:30 +00:00
if ( $env -> action !== " stats-update " )
update_statistics ( false );
2017-07-13 19:48:57 +00:00
}
]);
2017-09-16 16:31:30 +00:00
/**
* Updates the wiki ' s statistics .
* @ package feature - stats
* @ param boolean $update_all Whether all the statistics should be checked and recalculated , or just as many as we have time for according to the settings .
* @ param boolean $force Whether we should recalculate statistics that don ' t currently require recalculating anyway .
*/
function update_statistics ( $update_all = false , $force = false )
2017-07-13 19:48:57 +00:00
{
global $settings , $statistic_calculators ;
2017-09-16 16:31:30 +00:00
// Clear the existing statistics if we are asked to recalculate them all
if ( $force )
stats_save ( new stdClass ());
2017-07-13 19:48:57 +00:00
$stats = stats_load ();
$start_time = microtime ( true );
$stats_updated = 0 ;
foreach ( $statistic_calculators as $stat_id => $stat_calculator )
{
// If statistic doesn't exist or it's out of date then we should recalculate it.
// Otherwise, leave it and continue on to the next stat.
if ( ! empty ( $stats -> $stat_id ) && $start_time - $stats -> $stat_id -> lastupdated < $settings -> stats_update_interval )
continue ;
$mod_start_time = microtime ( true );
// Run the statistic calculator, passing in the existing stats data
$calculated = $stat_calculator [ " update " ]( ! empty ( $stats -> $stat_id ) ? $stats -> $stat_id : new stdClass ());
$new_stat_data = new stdClass ();
$new_stat_data -> id = $stat_id ;
$new_stat_data -> name = $stat_calculator [ " name " ];
$new_stat_data -> lastupdated = $calculated -> completed ? $mod_start_time : $stats -> $stat_id -> lastupdated ;
$new_stat_data -> value = $calculated -> value ;
if ( ! empty ( $calculated -> state ))
$new_stat_data -> state = $calculated -> state ;
// Save the new statistics
$stats -> $stat_id = $new_stat_data ;
$stats_updated ++ ;
2017-09-01 10:32:56 +00:00
if ( ! $update_all && microtime ( true ) - $start_time >= $settings -> stats_update_processingtime )
2017-07-13 19:48:57 +00:00
break ;
}
header ( " x-stats-recalculated: $stats_updated " );
//round((microtime(true) - $pageindex_read_start)*1000, 3)
header ( " x-stats-calctime: " . round (( microtime ( true ) - $start_time ) * 1000 , 3 ) . " ms " );
stats_save ( $stats );
}
/**
* Loads and returns the statistics cache file .
2017-09-15 22:06:10 +00:00
* @ package feature - stats
* @ return object The loaded & decoded statistics .
2017-07-13 19:48:57 +00:00
*/
function stats_load ()
{
global $paths ;
static $stats = null ;
if ( $stats == null )
$stats = file_exists ( $paths -> statsindex ) ? json_decode ( file_get_contents ( $paths -> statsindex )) : new stdClass ();
return $stats ;
}
/**
* Saves the statistics back to disk .
2017-09-15 22:06:10 +00:00
* @ package feature - stats
2017-07-13 19:48:57 +00:00
* @ param object The statistics cache to save .
* @ return bool Whether saving succeeded or not .
*/
function stats_save ( $stats )
{
global $paths ;
return file_put_contents ( $paths -> statsindex , json_encode ( $stats , JSON_PRETTY_PRINT ) . " \n " );
}