Version 5.0.13
This commit is contained in:
1 parent
b89858b2ff
commit
5efe744c5f
1407 files changed
+69690
-30177
No files matched your search
@@ -12,16 +12,31 @@ namespace IPS\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use DateInterval;
|
||||
use InvalidArgumentException;
|
||||
use IPS\Application;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\DateRange;
|
||||
use IPS\Helpers\Form\Select;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Platform\Bridge;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function array_keys;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_numeric;
|
||||
use function strval;
|
||||
use function time;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -37,7 +52,14 @@ abstract class Chart
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public ?string $controller = NULL;
|
||||
public ?string $controller = 'NULL';
|
||||
|
||||
/**
|
||||
* Whether this chart can be saved so that extra tabs exist. When this is true, the chart can only be saved to reports
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public bool $noTabs = false;
|
||||
|
||||
/**
|
||||
* Get Chart
|
||||
@@ -112,9 +134,9 @@ abstract class Chart
|
||||
{
|
||||
if ( !is_array( $data ) )
|
||||
{
|
||||
$data = Db::i()->select( '*', 'core_saved_charts', array( "chart_id=? AND chart_member=?", $data, $check->member_id ) )->first();
|
||||
$data = Db::i()->select( '*', 'core_saved_charts', array( "chart_id=? AND (chart_member=? OR chart_member IS NULL)", $data, $check->member_id ) )->first();
|
||||
}
|
||||
else if ( $data['chart_member'] !== $check->member_id )
|
||||
else if ( $data['chart_member'] !== $check->member_id and $data['chart_member'] !== null and $data['chart_member'] !== 0 )
|
||||
{
|
||||
throw new UnderflowException;
|
||||
}
|
||||
@@ -123,9 +145,9 @@ abstract class Chart
|
||||
{
|
||||
if ( !is_array( $data ) )
|
||||
{
|
||||
$data = Db::i()->select( '*','core_saved_charts', array( "chart_id=? AND chart_member=?", $data, Member::loggedIn()->member_id ) )->first();
|
||||
$data = Db::i()->select( '*', 'core_saved_charts', array( "chart_id=? AND (chart_member=? OR chart_member IS NULL OR chart_member=0)", $data, Member::loggedIn()->member_id ) )->first();
|
||||
}
|
||||
else if ( $data['chart_member'] !== Member::loggedIn()->member_id )
|
||||
else if ( $data['chart_member'] !== Member::loggedIn()->member_id and $data['chart_member'] !== null and $data['chart_member'] !== 0 )
|
||||
{
|
||||
throw new UnderflowException;
|
||||
}
|
||||
@@ -157,10 +179,14 @@ abstract class Chart
|
||||
$chart->showFilterTabs = FALSE;
|
||||
$chart->showIntervals = FALSE;
|
||||
$chart->showDateRange = FALSE;
|
||||
|
||||
$dateRange = static::getChartDateRanges( is_numeric( Request::i()->report_id ) ? (int) Request::i()->report_id : null );
|
||||
$chart->start = $dateRange['start'];
|
||||
$chart->end = $dateRange['end'];
|
||||
|
||||
return $chart;
|
||||
}
|
||||
catch( UnderflowException $e )
|
||||
catch( UnderflowException )
|
||||
{
|
||||
/* Chart doesn't exist, or isn't owned by $member */
|
||||
throw new OutOfRangeException;
|
||||
@@ -180,15 +206,34 @@ abstract class Chart
|
||||
public static function getChartsForMember( Url $url, bool $idsOnly = FALSE, ?Member $member = NULL ): array
|
||||
{
|
||||
$member ??= Member::loggedIn();
|
||||
|
||||
|
||||
if ( !$member->member_id )
|
||||
{
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach( Db::i()->select( '*', 'core_saved_charts', array( "chart_member=?", $member->member_id ) ) AS $chart )
|
||||
|
||||
if ( Bridge::i()->featureIsEnabled( "community_health_stats" ) and !empty( $url->queryString['communityhealth'] ) )
|
||||
{
|
||||
$charts = Bridge::i()->getCommunityHealthCharts();
|
||||
}
|
||||
else
|
||||
{
|
||||
$return = [];
|
||||
$reportID = is_numeric( Request::i()->report_id ) ? (int) Request::i()->report_id : 0;
|
||||
$where = [];
|
||||
if ( $reportID > 0 )
|
||||
{
|
||||
$where[] = array( "(chart_member IS NULL OR chart_member=? OR chart_member=0) AND chart_report_id=?", $member->member_id, $reportID );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( "(chart_member IS NULL OR chart_member=0) AND chart_report_id IS NULL", $member->member_id );
|
||||
}
|
||||
|
||||
$charts = Db::i()->select( '*', 'core_saved_charts', $where );
|
||||
}
|
||||
|
||||
foreach( $charts AS $chart )
|
||||
{
|
||||
if ( $idsOnly )
|
||||
{
|
||||
@@ -198,13 +243,12 @@ abstract class Chart
|
||||
{
|
||||
try
|
||||
{
|
||||
$controller = explode( '_', $chart['chart_controller'] );
|
||||
$return[$chart['chart_id']] = array(
|
||||
'chart' => static::constructMemberChartFromData( $chart, $url, $member ),
|
||||
'data' => $chart
|
||||
);
|
||||
}
|
||||
catch( OutOfRangeException )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -213,4 +257,222 @@ abstract class Chart
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the saved blocks for the current page.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSavedBlocks() : array
|
||||
{
|
||||
static $toDisplay = null;
|
||||
if ( !is_array( $toDisplay ) )
|
||||
{
|
||||
$where = [[ "(stat_member IS NULL OR stat_member=?)", Member::loggedIn()->member_id ]];
|
||||
if ( Request::i()->report_id )
|
||||
{
|
||||
$where[] = [ "stat_report_id=?", Request::i()->report_id ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = [ "stat_report_id IS NULL" ];
|
||||
}
|
||||
|
||||
$allExtensions = Application::allExtensions( 'core', 'OverviewStatistics', TRUE );
|
||||
$toDisplay = [];
|
||||
foreach ( Db::i()->select( "*", "core_saved_overview_stats", $where ) as $savedStat )
|
||||
{
|
||||
$key = $savedStat['stat_app'] . "_" . $savedStat['stat_extension'];
|
||||
if ( isset( $allExtensions[ $key ] ) and in_array( $savedStat['stat_subblock'], $allExtensions[ $key ]->getBlocks() ) )
|
||||
{
|
||||
$details = $allExtensions[ $key ]->getBlockDetails( $savedStat['stat_subblock'] );
|
||||
$details['title'] = $savedStat['stat_title'];
|
||||
|
||||
$toDisplay[] = [
|
||||
"blockKey" => $key,
|
||||
"subblock" => $savedStat['stat_subblock'],
|
||||
"saved_stat_id" => $savedStat['id'],
|
||||
"details" => $details,
|
||||
'page' => $allExtensions[ $key ]->page,
|
||||
'extension' => $allExtensions[$key],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $toDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date range selector that is at the top of the saved reports acp page
|
||||
* @return string
|
||||
*/
|
||||
public static function getBlockDateRangeForm( ?int $reportId=null ) : string
|
||||
{
|
||||
$options = array(
|
||||
'7' => 'last_week',
|
||||
'30' => 'last_month',
|
||||
'90' => 'last_three_months',
|
||||
'180' => 'last_six_months',
|
||||
'365' => 'last_year',
|
||||
'0' => 'alltime',
|
||||
'-1' => 'custom'
|
||||
);
|
||||
|
||||
$defaultRange = '7';
|
||||
$dateRange = null;
|
||||
if ( ( is_numeric( Request::i()->predate ) and isset( $options[Request::i()->predate] ) ) or ( is_numeric( @Request::i()->date['start'] ) and is_numeric( @Request::i()->date['end'] ) ) )
|
||||
{
|
||||
if ( is_numeric( @Request::i()->date['start'] ) and is_numeric( @Request::i()->date['end'] ) )
|
||||
{
|
||||
$dateRange = [
|
||||
'start' => Request::i()->date['start'],
|
||||
'end' => Request::i()->date['end']
|
||||
];
|
||||
|
||||
$defaultRange = '-1';
|
||||
}
|
||||
else
|
||||
{
|
||||
$defaultRange = Request::i()->predate;
|
||||
}
|
||||
}
|
||||
else if ( is_int( $reportId ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$currentReport = Db::i()->select( '*', 'core_reports', ['id=?', $reportId] )->first();
|
||||
$defaultRange = strval($currentReport['report_range_timescale'] ?? '7');
|
||||
if ( isset( $currentReport['report_range_start'] ) or isset( $currentReport['report_range_end'] ) )
|
||||
{
|
||||
$dateRange = [
|
||||
'start'=> $currentReport['report_range_start'],
|
||||
'end' => $currentReport['report_range_end'],
|
||||
];
|
||||
}
|
||||
}
|
||||
catch ( \UnderflowException ) {}
|
||||
}
|
||||
|
||||
$form = new Form( 'posts', 'update' );
|
||||
$form->add( new Select( 'predate', $defaultRange, FALSE, array( 'options' => $options, 'toggles' => array( '-1' => array( 'dateFilterInputs' ) ) ) ) );
|
||||
$form->add( new DateRange( 'date', $dateRange, FALSE, array(), NULL, NULL, NULL, 'dateFilterInputs' ) );
|
||||
$form->class .= " ipsBox ipsPull i-padding_2";
|
||||
|
||||
if ( is_int( $reportId ) and $values = $form->values() )
|
||||
{
|
||||
$update = [
|
||||
'report_range_timescale' => (int) $values['predate'],
|
||||
'report_range_start' => @$values['date']['start']?->getTimestamp() ?: null,
|
||||
'report_range_end' => @$values['date']['end']?->getTimestamp() ?: null
|
||||
];
|
||||
Db::i()->update( 'core_reports', $update, ['id=?', $reportId] );
|
||||
if ( Request::i()->isAjax() )
|
||||
{
|
||||
Output::i()->json( ['message' => 'ok'], 200 );
|
||||
}
|
||||
Output::i()->redirect( (string) Url::internal( 'app=core&module=overview&controller=mycharts' ) );
|
||||
}
|
||||
|
||||
return $form->customTemplate( array( Theme::i()->getTemplate( 'stats' ), 'filtersOverviewForm' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAppsToFilterBy()
|
||||
{
|
||||
static $apps = null;
|
||||
if ( !is_array( $apps ) )
|
||||
{
|
||||
$blocks = static::getSavedBlocks();
|
||||
$apps = [];
|
||||
foreach ( $blocks as $block )
|
||||
{
|
||||
if ( isset( $block['details']['app'] ) and !isset( $apps[ $block['details']['app'] ] ) )
|
||||
{
|
||||
$apps[ $block['details']['app'] ] = Application::load( $block['details']['app'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $apps;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int|null $reportId The ID of the report, or leave null to use the default saved charts function
|
||||
* @return array{start: DateTime|null, end: DateTime|null}
|
||||
*/
|
||||
public static function getChartDateRanges( ?int $reportId=null )
|
||||
{
|
||||
// It is possible this is included right in the request
|
||||
if ( isset( Request::i()->predate ) and is_numeric( Request::i()->predate ) and intval( Request::i()->predate ) >= 0 )
|
||||
{
|
||||
$days = (int) Request::i()->predate;
|
||||
if ( $days <= 0 )
|
||||
{
|
||||
return [
|
||||
'start' => null,
|
||||
'end' => null,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'end' => DateTime::ts( time() )->setTime( 23, 59, 59 ),
|
||||
'start' => DateTime::ts( time() )->setTime( 0, 0, 0 )->sub( new DateInterval( "P{$days}D" ) )
|
||||
];
|
||||
}
|
||||
else if ( isset( Request::i()->date['start'] ) and isset( Request::i()->date['end'] ) ) // the way the JS works, the start and end are sent even if it just relies on the predate.
|
||||
{
|
||||
$start = is_numeric( Request::i()->date['start'] ) ? DateTime::ts( (int) Request::i()->date['start'] ) : null;
|
||||
$end = is_numeric( Request::i()->date['end'] ) ? DateTime::ts( (int) Request::i()->date['end'] ) : null;
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
];
|
||||
}
|
||||
|
||||
if ( is_null( $reportId ) )
|
||||
{
|
||||
if ( isset( Settings::i()->core_saved_charts_range_timescale ) and is_numeric( Settings::i()->core_saved_charts_range_timescale ) and intval( Settings::i()->core_saved_charts_range_timescale ) >= 0 )
|
||||
{
|
||||
$days = (int) Settings::i()->core_saved_charts_range_timescale;
|
||||
if ( $days <= 0 )
|
||||
{
|
||||
return [
|
||||
'start' => null,
|
||||
'end' => null,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'end' => DateTime::ts( time() )->setTime( 23, 59, 59 ),
|
||||
'start' => DateTime::ts( time() )->setTime( 0, 0, 0 )->sub( new DateInterval( "P{$days}D" ) )
|
||||
];
|
||||
}
|
||||
|
||||
$start = is_numeric( Settings::i()->core_saved_charts_range_start ) ? DateTime::ts( (int) Settings::i()->core_saved_charts_range_start ) : null;
|
||||
$end = is_numeric( Settings::i()->core_saved_charts_range_end ) ? DateTime::ts( (int) Settings::i()->core_saved_charts_range_end ) : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$row = Db::i()->select( "*", "core_reports", [ "id=?", $reportId ?: 0 ] )->first();
|
||||
$start = is_int( $row['report_range_start'] ) ? DateTime::ts( $row['report_range_start'] ) : null;
|
||||
$end = is_int( $row['report_range_end'] ) ? DateTime::ts( $row['end'] ) : null;
|
||||
}
|
||||
catch ( UnderflowException $e )
|
||||
{
|
||||
$start = null;
|
||||
$end = null;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"start" => $start,
|
||||
"end" => $end
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user