Version 4.7.13
This commit is contained in:
1 parent
fd22d99e69
commit
7124a02564
954 files changed
+29277
-59308
No files matched your search
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief ACP Notification Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 08 Mar 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\AdminNotifications;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ACP Notification Extension
|
||||
*/
|
||||
class _AccountDeletion extends \IPS\core\AdminNotification
|
||||
{
|
||||
/**
|
||||
* @brief Identifier for what to group this notification type with on the settings form
|
||||
*/
|
||||
public static $group = 'members';
|
||||
|
||||
/**
|
||||
* @brief Priority 1-5 (1 being highest) for this group compared to others
|
||||
*/
|
||||
public static $groupPriority = 2;
|
||||
|
||||
/**
|
||||
* @brief Priority 1-5 (1 being highest) for this notification type compared to others in the same group
|
||||
*/
|
||||
public static $itemPriority = 1;
|
||||
|
||||
/**
|
||||
* Title for settings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function settingsTitle()
|
||||
{
|
||||
return 'acp_notification_AccountDeletion';
|
||||
}
|
||||
|
||||
/**
|
||||
* Can a member access this type of notification?
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @return bool
|
||||
*/
|
||||
public static function permissionCheck( \IPS\Member $member ): bool
|
||||
{
|
||||
return $member->hasAcpRestriction( 'core', 'members' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this type of notification ever optional (controls if it will be selectable as "viewable" in settings)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function mayBeOptional()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this type of notification might recur (controls what options will be available for the email setting)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function mayRecur(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Title (full HTML, must be escaped where necessary)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
$others = \IPS\Db::i()->select( 'COUNT(*)', 'core_member_privacy_actions', [ 'action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE ] )->first();
|
||||
$names = [];
|
||||
foreach(
|
||||
\IPS\Db::i()->select(
|
||||
'*',
|
||||
'core_member_privacy_actions',
|
||||
where: [ 'action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE ],
|
||||
order: 'request_date asc',
|
||||
limit: [ 0, 2 ]
|
||||
)->join(
|
||||
'core_members',
|
||||
'core_member_privacy_actions.member_id=core_members.member_id'
|
||||
) as $user
|
||||
)
|
||||
{
|
||||
$names[ $user[ 'member_id' ] ] = htmlentities( $user[ 'name' ], ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
$others--;
|
||||
}
|
||||
|
||||
if( $others )
|
||||
{
|
||||
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'and_x_others', FALSE, [ 'pluralize' => [ $others ] ] );
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'account_deletion_request_adminnotification', FALSE, [ 'pluralize' => [ \count( $names ) ], 'sprintf' => [ \IPS\Member::loggedIn()->language()->formatList( $names ) ] ] );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Body (full HTML, must be escaped where necessary)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function body(): string
|
||||
{
|
||||
$users = [];
|
||||
|
||||
$names = [];
|
||||
foreach(
|
||||
\IPS\Db::i()->select(
|
||||
'*',
|
||||
'core_member_privacy_actions',
|
||||
where: ['action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE],
|
||||
order: 'request_date asc',
|
||||
limit: [0, 2]
|
||||
)->join(
|
||||
'core_members',
|
||||
'core_member_privacy_actions.member_id=core_members.member_id'
|
||||
) as $user
|
||||
)
|
||||
{
|
||||
$users[ $user[ 'member_id' ] ] = \IPS\Member::constructFromData( $user );
|
||||
$users[ $user[ 'member_id' ] ]->_privacy_id = $user['id'];
|
||||
}
|
||||
|
||||
if( \count( $users ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'notifications', 'core', 'admin' )->accountDeletionRequest( $users );
|
||||
}else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Severity
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function severity(): string
|
||||
{
|
||||
return static::SEVERITY_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismissible?
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dismissible(): string
|
||||
{
|
||||
return static::DISMISSIBLE_TEMPORARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Style
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function style(): string
|
||||
{
|
||||
return static::STYLE_INFORMATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick link from popup menu
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function link(): \IPS\Http\Url
|
||||
{
|
||||
return \IPS\Http\Url::internal( 'app=core&module=members&controller=privacy&filter=deletion_request' );
|
||||
}
|
||||
}
|
||||
@@ -707,5 +707,9 @@ class _ConfigurationError extends \IPS\core\AdminNotification
|
||||
$login = new \IPS\Login;
|
||||
return !( $login->authType() & \IPS\Login::AUTH_TYPE_USERNAME );
|
||||
}
|
||||
elseif( $this->extra == 'tasksNotRunning' AND \IPS\CIC )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief ACP Notification Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 08 Mar 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\AdminNotifications;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ACP Notification Extension
|
||||
*/
|
||||
class _PiiDataRequest extends \IPS\core\AdminNotification
|
||||
{
|
||||
/**
|
||||
* @brief Identifier for what to group this notification type with on the settings form
|
||||
*/
|
||||
public static $group = 'members';
|
||||
|
||||
/**
|
||||
* @brief Priority 1-5 (1 being highest) for this group compared to others
|
||||
*/
|
||||
public static $groupPriority = 2;
|
||||
|
||||
/**
|
||||
* @brief Priority 1-5 (1 being highest) for this notification type compared to others in the same group
|
||||
*/
|
||||
public static $itemPriority = 1;
|
||||
|
||||
/**
|
||||
* Title for settings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function settingsTitle(): string
|
||||
{
|
||||
return 'acp_notification_PiiDataRequest';
|
||||
}
|
||||
|
||||
/**
|
||||
* Can a member access this type of notification?
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @return bool
|
||||
*/
|
||||
public static function permissionCheck( \IPS\Member $member ): bool
|
||||
{
|
||||
return $member->hasAcpRestriction( 'core', 'members' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this type of notification ever optional (controls if it will be selectable as "viewable" in settings)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function mayBeOptional()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this type of notification might recur (controls what options will be available for the email setting)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function mayRecur(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Title (full HTML, must be escaped where necessary)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title()
|
||||
{
|
||||
$others = \IPS\Db::i()->select( 'COUNT(*)', 'core_member_privacy_actions', [ 'action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_PII ] )->first();
|
||||
$names = [];
|
||||
foreach (
|
||||
\IPS\Db::i()->select(
|
||||
"*",
|
||||
'core_member_privacy_actions',
|
||||
where: [ 'action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_PII ],
|
||||
order: 'request_date asc',
|
||||
limit: [ 0, 2 ]
|
||||
)->join(
|
||||
'core_members',
|
||||
'core_member_privacy_actions.member_id=core_members.member_id'
|
||||
) as $user
|
||||
)
|
||||
{
|
||||
$names[ $user['member_id'] ] = htmlentities( $user['name'], ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
$others--;
|
||||
}
|
||||
if ( $others )
|
||||
{
|
||||
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'and_x_others', FALSE, array( 'pluralize' => array( $others ) ) );
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'pii_data_request_adminnotification', FALSE, array( 'pluralize' => array( \count( $names ) ), 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ) ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Body (full HTML, must be escaped where necessary)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function body(): string
|
||||
{
|
||||
$users = [];
|
||||
|
||||
$names = [];
|
||||
foreach(
|
||||
\IPS\Db::i()->select(
|
||||
'*',
|
||||
'core_member_privacy_actions',
|
||||
where: ['action=?', \IPS\Member\PrivacyAction::TYPE_REQUEST_PII],
|
||||
order: 'request_date asc',
|
||||
limit: [0, 2]
|
||||
)->join(
|
||||
'core_members',
|
||||
'core_member_privacy_actions.member_id=core_members.member_id'
|
||||
) as $user
|
||||
)
|
||||
{
|
||||
$users[ $user[ 'member_id' ] ] = \IPS\Member::constructFromData( $user );
|
||||
$users[ $user[ 'member_id' ] ]->_privacy_id = $user['id'];
|
||||
}
|
||||
|
||||
if( \count( $users ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'notifications', 'core', 'admin' )->piiRequest( $users );
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Severity
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function severity(): string
|
||||
{
|
||||
return static::SEVERITY_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismissible?
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dismissible(): string
|
||||
{
|
||||
return static::DISMISSIBLE_TEMPORARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Style
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function style(): string
|
||||
{
|
||||
return static::STYLE_INFORMATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick link from popup menu
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function link(): \IPS\Http\Url
|
||||
{
|
||||
return \IPS\Http\Url::internal( 'app=core&module=members&controller=privacy&filter=pii_data' );
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,9 @@ class _Ckeditor
|
||||
{
|
||||
throw new \UnexpectedValueException( \IPS\Member::loggedIn()->language()->addToStack('dev_build_ckeditor_exists_err', FALSE, array( 'sprintf' => array( "{$path}/applications/core/interface/ckeditor/ckeditor" ) ) ) );
|
||||
}
|
||||
|
||||
$command = "{$_javaPath}java -jar \"{$path}/applications/core/dev/ckbuilder.jar\" --build \"{$path}/applications/core/dev/ckeditor\" \"{$path}/applications/core/interface/ckeditor\" --build-config \"{$path}/applications/core/dev/ckeditor/build-config.js\" --no-zip --no-tar --overwrite 2>&1";
|
||||
|
||||
$version = \IPS\Helpers\Form\Editor::ckeditorVersion();
|
||||
$command = "{$_javaPath}java -jar \"{$path}/applications/core/dev/ckbuilder.jar\" --build \"{$path}/applications/core/dev/ckeditor\" \"{$path}/applications/core/interface/ckeditor\" --build-config \"{$path}/applications/core/dev/ckeditor/build-config.js\" --no-zip --no-tar --version {$version} --overwrite 2>&1";
|
||||
$output = array();
|
||||
\exec( $command, $output );
|
||||
|
||||
|
||||
@@ -61,17 +61,6 @@ class _Giphy
|
||||
public function edit()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'giphy_custom_apikey', (int) ! ( \IPS\Settings::i()->giphy_apikey_default == \IPS\Settings::i()->giphy_apikey OR ! \IPS\Settings::i()->giphy_apikey ), TRUE, array(
|
||||
'options' => array(
|
||||
'0' => 'giphy_custom_apikey_default',
|
||||
'1' => 'giphy_custom_apikey_custom',
|
||||
),
|
||||
'toggles' => array(
|
||||
'0' => array(),
|
||||
'1' => array( 'giphy_apikey' ),
|
||||
)
|
||||
) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'giphy_apikey', \IPS\Settings::i()->giphy_apikey ? \IPS\Settings::i()->giphy_apikey : '', FALSE, array(), NULL, NULL, NULL, 'giphy_apikey' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'giphy_rating', \IPS\Settings::i()->giphy_rating ? \IPS\Settings::i()->giphy_rating : 'x', FALSE, array(
|
||||
@@ -90,7 +79,7 @@ class _Giphy
|
||||
try
|
||||
{
|
||||
/* Enable giphy automatically on the first submit of the form and add it automatically to all toolbars */
|
||||
if ( ! \IPS\Settings::i()->giphy_enabled )
|
||||
if ( ! \IPS\Settings::i()->giphy_enabled AND \IPS\Settings::i()->giphy_apikey != '' )
|
||||
{
|
||||
$values['giphy_enabled'] = 1;
|
||||
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Enhancements
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\CommunityEnhancements;
|
||||
|
||||
use IPS\core\Hive;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Login;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Settings;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Enhancement
|
||||
*/
|
||||
class _Hive
|
||||
{
|
||||
/**
|
||||
* @brief Enhancement is enabled?
|
||||
*/
|
||||
public $enabled = FALSE;
|
||||
|
||||
/**
|
||||
* @brief IPS-provided enhancement?
|
||||
*/
|
||||
public $ips = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Enhancement has configuration options?
|
||||
*/
|
||||
public $hasOptions = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Icon data
|
||||
*/
|
||||
public $icon = "communityhive.svg";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->enabled = ( !empty( Settings::i()->hive_enabled ) AND !empty( Settings::i()->hive_key ) AND !empty( Settings::i()->hive_site_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$hostname = \defined( 'COMMUNITYHIVE_URL' ) ? COMMUNITYHIVE_URL : 'https://communityhive.com';
|
||||
$output = '';
|
||||
|
||||
if( empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form('hive', 'enable' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'hive_community_name', Settings::i()->board_name, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'hive_email_address', Member::loggedIn()->email, TRUE ) );
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$randomKey = Login::generateRandomString( 40 );
|
||||
$request = Url::external( $hostname . '/api/v1/activate')
|
||||
->request()
|
||||
->put( json_encode([
|
||||
'site_name' => $values['hive_community_name'],
|
||||
'site_url' => Settings::i()->base_url,
|
||||
'site_api_url' => (string) Url::internal('app=core&module=hive&controller=content', 'front', 'hive_content'),
|
||||
'site_key' => $randomKey,
|
||||
'site_email' => $values['hive_email_address'],
|
||||
'site_software' => 'invisioncommunity',
|
||||
'hive_system_version' => \IPS\Application::load('core')->version
|
||||
]) );
|
||||
|
||||
if( (int) $request->httpResponseCode !== 200 )
|
||||
{
|
||||
throw new \LogicException( $request->decodeJson()['error'] ?? 'There was an issue connecting to Community Hive.' );
|
||||
}
|
||||
|
||||
$response = $request->decodeJson();
|
||||
|
||||
Settings::i()->changeValues([
|
||||
'hive_key' => $response['hive_key'],
|
||||
'hive_site_id' => $response['hive_site_id'],
|
||||
'hive_site_key' => $randomKey,
|
||||
'hive_enabled' => TRUE
|
||||
]);
|
||||
|
||||
/* Subscribe site manager */
|
||||
$response = Hive::api( 'subscribe', [
|
||||
'site_member_id' => Member::loggedIn()->member_id,
|
||||
'group_hash' => Hive::groupHash( Member::loggedin() ),
|
||||
'member_email' => $values['hive_email_address'],
|
||||
]);
|
||||
|
||||
/* Save subscribe */
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_hive_subscribers', [ 'member_id' => Member::loggedIn()->member_id, 'subscribe_date' => time() ] );
|
||||
}
|
||||
catch( Db\Exception $e ) {}
|
||||
|
||||
$redirectUrl = Url::external( $response['redirect_url'] );
|
||||
$output = \IPS\Theme::i()->getTemplate( 'hive', 'core', 'admin' )->wrapper( 'activated', null, $redirectUrl );
|
||||
}
|
||||
catch ( \LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
$output = \IPS\Theme::i()->getTemplate( 'hive', 'core', 'admin' )->wrapper( 'activation', $form );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output = \IPS\Theme::i()->getTemplate( 'hive', 'core', 'admin' )->wrapper( 'activation', $form );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$form = new \IPS\Helpers\Form('hive', 'save' );
|
||||
|
||||
/* Work out all the different classes */
|
||||
$classes = array();
|
||||
|
||||
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
|
||||
{
|
||||
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) AND isset( $class::$databaseColumnMap['date'] ) )
|
||||
{
|
||||
$classes[ $class ] = $class::$title . '_pl';
|
||||
}
|
||||
}
|
||||
|
||||
$settings = json_decode( Settings::i()->hive_content, TRUE );
|
||||
|
||||
/* Work out all the different classes */
|
||||
$classes = array();
|
||||
$classToggles = array();
|
||||
|
||||
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
|
||||
{
|
||||
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) AND isset( $class::$databaseColumnMap['date'] ) )
|
||||
{
|
||||
$bits = explode( '\\', $class );
|
||||
if( method_exists( $class, 'database' ) )
|
||||
{
|
||||
$label = $class::database()->_title . ' - ' . Member::loggedIn()->language()->addToStack( $class::$title . '_pl' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$label = Member::loggedIn()->language()->addToStack( '__app_' . $bits[1] ) . ' - ' . Member::loggedIn()->language()->addToStack( $class::$title . '_pl' );
|
||||
}
|
||||
$classes[ $class ] = $label;
|
||||
if ( isset( $class::$containerNodeClass ) )
|
||||
{
|
||||
$classToggles[ $class ][] = 'hive_containers_' . $class::$title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$containers = $settings['content_containers'] ?? [];
|
||||
|
||||
/* Add the fields for them */
|
||||
$default = (!isset( $settings['content_classes'] ) OR $settings['content_classes'] === '*') ? '*' : explode( ',', $settings['content_classes'] );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'hive_content_classes', $default, NULL, array( 'options' => $classes, 'unlimited' => '*', 'unlimitedLang' => 'all', 'impliedUnlimited' => TRUE, 'toggles' => $classToggles ), NULL, NULL, NULL, 'hive_classes' ) );
|
||||
|
||||
/* Nodes */
|
||||
foreach ( $classToggles as $class => $id )
|
||||
{
|
||||
if ( isset( $class::$containerNodeClass ) )
|
||||
{
|
||||
$nodeClass = $class::$containerNodeClass;
|
||||
$value = isset( $containers[ $class ] ) ? implode( ',', $containers[ $class ] ) : 0;
|
||||
|
||||
$field = new \IPS\Helpers\Form\Node( 'hive_containers_' . $class::$title, $value, FALSE, array( 'class' => $nodeClass, 'clubs' => TRUE, 'multiple' => TRUE, 'zeroVal' => 'all', 'permissionCheck' => $nodeClass::searchableNodesPermission(), 'forceOwner' => FALSE, 'subnodes' => FALSE ), NULL, NULL, NULL, 'hive_containers_' . $class::$title );
|
||||
|
||||
if( method_exists( $class, 'database' ) )
|
||||
{
|
||||
$field->label = $class::database()->_title . ' - ' . Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle );
|
||||
}
|
||||
else
|
||||
{
|
||||
$field->label = Member::loggedIn()->language()->addToStack( $class::$title . '_pl' ) . ' - ' . Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle );
|
||||
}
|
||||
$form->add( $field );
|
||||
}
|
||||
}
|
||||
|
||||
if( $values = $form->values() )
|
||||
{
|
||||
if ( isset( $values['hive_content_classes'] ) )
|
||||
{
|
||||
$containers = NULL;
|
||||
foreach ( ( $values['hive_content_classes'] == '*' ? array_keys( $classes ) : $values['hive_content_classes'] ) as $class )
|
||||
{
|
||||
if ( isset( $values[ 'hive_containers_' . $class::$title ] ) and $values[ 'hive_containers_' . $class::$title ] )
|
||||
{
|
||||
$containers[ $class ] = array_keys( $values[ 'hive_containers_' . $class::$title ] );
|
||||
}
|
||||
}
|
||||
|
||||
$settings['content_containers'] = $containers;
|
||||
}
|
||||
|
||||
$settings['content_classes'] = \is_array( $values['hive_content_classes'] ) ? implode( ',', $values['hive_content_classes'] ) : $values['hive_content_classes'];
|
||||
Settings::i()->changeValues([ 'hive_content' => json_encode( $settings ) ]);
|
||||
Output::i()->inlineMessage = Member::loggedIn()->language()->addToStack('saved');
|
||||
}
|
||||
|
||||
$output = \IPS\Theme::i()->getTemplate( 'hive', 'core', 'admin' )->wrapper( 'config', $form );
|
||||
}
|
||||
|
||||
Output::i()->sidebar['actions'] = array(
|
||||
'help' => array(
|
||||
'title' => 'help',
|
||||
'icon' => 'question-circle',
|
||||
'link' => Url::external( $hostname ),
|
||||
'target' => '_blank'
|
||||
),
|
||||
);
|
||||
|
||||
Output::i()->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
* @param $enabled bool Enable/Disable
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function toggle( $enabled )
|
||||
{
|
||||
Settings::i()->changeValues([ 'hive_enabled' => (bool) $enabled ]);
|
||||
|
||||
if( empty( Settings::i()->hive_key ) OR empty( Settings::i()->hive_site_id ) )
|
||||
{
|
||||
throw new \LogicException( 'error' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Settings
|
||||
*
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function testSettings()
|
||||
{
|
||||
if ( FALSE )
|
||||
{
|
||||
throw new \LogicException( 'error' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,31 +40,11 @@ class _Registrations
|
||||
public function getBlock()
|
||||
{
|
||||
/* We can use the registration stats controller for this */
|
||||
$chart = new \IPS\Helpers\Chart\Database(
|
||||
\IPS\Http\Url::internal( 'app=core&module=stats&controller=registrationstats' ),
|
||||
'core_members',
|
||||
'joined',
|
||||
'',
|
||||
array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#0c849f' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#dce0e7' ) ),
|
||||
'vAxis' => array( 'gridlines' => array( 'color' => '#dce0e7' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'ColumnChart',
|
||||
'weekly',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P30D' ) ), 'end' => \IPS\DateTime::ts( time() ) )
|
||||
);
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_new_registrations'), 'number', 'COUNT(*)', FALSE );
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
/* fetch only successful registered members ; if this needs to be changed, please review the other areas where we have the name<>? AND email<>? condition */
|
||||
$chart->where[] = array( 'completed=?', true );
|
||||
|
||||
/* Output */
|
||||
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart );
|
||||
$chart = \IPS\core\Statistics\Chart::loadFromExtension( 'core', 'Registrations' )->getChart( \IPS\Http\Url::internal( 'app=core&module=stats&controller=registrationstats' ) );
|
||||
$chart->showFilterTabs = FALSE;
|
||||
$chart->showIntervals = FALSE;
|
||||
$chart->showDateRange = FALSE;
|
||||
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart );;
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class _AdminLoginLogs
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class _AdminLogs
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Dnames
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_member_history', [ 'log_ip_address=?', '' ] , [ 'log_ip_address != "" AND log_type=? AND log_date <?', 'display_name', $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Dnames
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -97,6 +108,6 @@ class _Dnames
|
||||
*/
|
||||
public function findByMember( $member )
|
||||
{
|
||||
return \IPS\Db::i()->select( "log_ip_address AS ip, count(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_type=? AND log_member=?', 'display_name', $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' );
|
||||
return iterator_to_array( \IPS\Db::i()->select( "log_ip_address AS ip, count(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_type=? AND log_member=? AND log_by=?', 'display_name', $member->member_id, $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' ) );
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class _ErrorLogs
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Logins
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_members_known_ip_addresses', [ 'ip_address=?', '' ] , [ "ip_address != '' AND last_seen <?", $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Logins
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _MemberHistory
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_member_history', [ 'log_ip_address=?', '' ] , [ "log_ip_address != '' AND log_date <?", $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _MemberHistory
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class _Messages
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -114,6 +114,6 @@ class _Messages
|
||||
*/
|
||||
public function findByMember( $member )
|
||||
{
|
||||
return \IPS\Db::i()->select( "ip_address AS ip, count(*) AS count, MIN(vote_date) AS first, MAX(vote_date) AS last", 'core_voters', array( 'member_id=?', $member->member_id ), NULL, NULL, 'ip_address' )->setKeyField( 'ip' );
|
||||
return \IPS\Db::i()->select( "msg_ip_address AS ip, count(*) AS count, MIN(msg_date) AS first, MAX(msg_date) AS last", 'core_message_posts', array( 'msg_author_id=?', $member->member_id ), NULL, NULL, 'ip' )->setKeyField( 'ip' );
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class _ModeratorLogs
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Ratings
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_ratings', [ 'ip=?', '' ] , [ "ip != '' AND rating_date <?", $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Ratings
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Registration
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_members', [ 'ip_address=?', '' ] , [ "ip_address != '' AND joined <?", $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Registration
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _ReportComments
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_rc_comments', [ 'ip_address=?', '' ] , [ 'comment_date <?', $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _ReportComments
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Reports
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_rc_reports', [ 'ip_address=?', '' ] , [ 'date_reported <?', $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Reports
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class _SpamLogs
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class _Validations
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Votes
|
||||
{
|
||||
/**
|
||||
* Removes the logged IP address
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function pruneIpAddresses(int $time)
|
||||
{
|
||||
\IPS\Db::i()->update('core_voters', [ 'ip_address=?', '' ] , [ "ip_address != '' AND vote_date <?", $time ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported in the ACP IP address lookup tool?
|
||||
*
|
||||
@@ -39,7 +50,7 @@ class _Votes
|
||||
* @return bool
|
||||
* @note If the method does not exist in an extension, the result is presumed to be TRUE
|
||||
*/
|
||||
public function supportedInModCp()
|
||||
public function supportedInModCp(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,13 @@ class _Groups extends \IPS\core\MemberACPProfile\Block
|
||||
'data-adminGroups' => json_encode( iterator_to_array( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_id_type=?', 'group' ) ) ) )
|
||||
)) ;
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'group', $this->member->member_group_id, TRUE, array( 'options' => $availableGroups, 'parse' => 'normal' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Enum( 'secondary_groups', array_filter( explode( ',', $this->member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'parse' => 'normal' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Enum( 'secondary_groups', array_filter( explode( ',', $this->member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'parse' => 'normal' ), function( $val ) {
|
||||
/* There is a limit to the amount of groups that can be stored in mgroup_others. For now, limit how many can be selected and in the future change to a mapping table instead. */
|
||||
if ( count( $val ) > 50 )
|
||||
{
|
||||
throw new \DomainException( 'too_many_groups' );
|
||||
}
|
||||
} ) );
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
|
||||
@@ -75,7 +75,7 @@ class _Main
|
||||
{
|
||||
try
|
||||
{
|
||||
$log = \IPS\Db::i()->select( '*', 'core_members_known_ip_addresses', array( 'member_id=? AND device_key=?', \IPS\Member::loggedIn()->member_id, $device->device_key ), 'last_seen DESC' )->first();
|
||||
$log = \IPS\Db::i()->select( '*', 'core_members_known_ip_addresses', array( 'member_id=? AND device_key=?', $member->member_id, $device->device_key ), 'last_seen DESC' )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -107,7 +107,19 @@ class _Main
|
||||
'date' => \IPS\DateTime::ts( $row['log_date'] )->rfc3339()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* PII Profile Fields */
|
||||
$fieldValues = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
|
||||
|
||||
foreach ( \IPS\core\ProfileFields\Field::values( $fieldValues, \IPS\core\ProfileFields\Field::PII_DATA_EXPORT ) as $group => $profielFields )
|
||||
{
|
||||
foreach( $profielFields as $fieldID => $value )
|
||||
{
|
||||
$return['pfield_'. $fieldID] = $value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ class _ProfileFields
|
||||
break;
|
||||
case 'IPS\Helpers\Form\Select':
|
||||
case 'IPS\Helpers\Form\Radio':
|
||||
case 'IPS\Helpers\Form\CheckboxSet':
|
||||
if( $field['pf_multiple'] )
|
||||
{
|
||||
$options = array();
|
||||
@@ -130,6 +129,11 @@ class _ProfileFields
|
||||
|
||||
$helper = new \IPS\Helpers\Form\Select( $langKey, ( isset( $criteria[ $langKey ] ) ) ? $criteria[ $langKey ] : NULL, FALSE, array( 'options' => $options, 'multiple' => ( $field['pf_multiple'] ) ? TRUE : FALSE, 'noDefault' => true ) );
|
||||
break;
|
||||
|
||||
case 'IPS\Helpers\Form\CheckboxSet':
|
||||
$options = json_decode( $field['pf_content'], true );
|
||||
$helper = new \IPS\Helpers\Form\Select( $langKey, ( isset( $criteria[ $langKey ] ) ) ? $criteria[ $langKey ] : NULL, FALSE, array( 'options' => $options, 'multiple' => TRUE, 'noDefault' => true ) );
|
||||
break;
|
||||
}
|
||||
|
||||
if( $helper )
|
||||
|
||||
@@ -46,7 +46,8 @@ class _Core
|
||||
'terms_acceptance',
|
||||
'points',
|
||||
'badges',
|
||||
'club_membership'
|
||||
'club_membership',
|
||||
'privacy',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -441,7 +442,18 @@ class _Core
|
||||
$spamDefenseScore = NULL;
|
||||
if ( isset( $jsonValue['spamCode'] ) AND isset( $jsonValue['spamAction'] ) )
|
||||
{
|
||||
$spamDefenseScore = \IPS\Member::loggedIn()->language()->addToStack( 'history_spam_defense_' . $jsonValue['spamAction'], FALSE, array( 'sprintf' => array( $jsonValue['spamCode'] ) ) );
|
||||
if ( isset( $jsonValue['disposable'] ) AND $jsonValue['disposable'] )
|
||||
{
|
||||
$spamDefenseScore = \IPS\Member::loggedIn()->language()->addToStack( 'history_spam_defense_disposable_' . $jsonValue['spamAction'] );
|
||||
}
|
||||
elseif ( isset( $jsonValue['geoBlock'] ) AND $jsonValue['geoBlock'] )
|
||||
{
|
||||
$spamDefenseScore = \IPS\Member::loggedIn()->language()->addToStack( 'history_spam_defense_geoblock' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$spamDefenseScore = \IPS\Member::loggedIn()->language()->addToStack( 'history_spam_defense_' . $jsonValue['spamAction'], FALSE, array( 'sprintf' => array( $jsonValue['spamCode'] ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $jsonValue['type'] )
|
||||
@@ -762,6 +774,23 @@ class _Core
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
case 'privacy':
|
||||
if( $jsonValue == 'account_deletion_cancelled' ) // This may not have been properly structured in some previous versions [4.7.11.x]
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'account_deletion_cancelled', FALSE, [ 'htmlsprintf' => [ $byMember ?: $byStaff ] ] );
|
||||
}
|
||||
|
||||
switch ( $jsonValue['type'] )
|
||||
{
|
||||
case 'account_deletion_requested':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_deletion_request', FALSE, [ 'htmlsprintf' => [ $byMember ?: $byStaff ] ] );
|
||||
case 'account_deletion_cancelled':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'account_deletion_cancelled', FALSE, [ 'htmlsprintf' => [ $byMember ?: $byStaff ] ] );
|
||||
case 'pii_data_request':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'history_pii_data_request', FALSE, [ 'htmlsprintf' => [ $byMember ?: $byStaff ] ] );
|
||||
case 'pii_download':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'history_pii_downloaded', FALSE, [ 'htmlsprintf' => [ $byMember ?: $byStaff ] ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member Sync
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MemberSync;
|
||||
use IPS\core\Hive;
|
||||
use IPS\Db;
|
||||
use IPS\Member;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Sync
|
||||
*/
|
||||
class _Hive
|
||||
{
|
||||
/**
|
||||
* Member account has been updated
|
||||
*
|
||||
* @param $member Member Member updating profile
|
||||
* @param $changes array The changes
|
||||
* @return void
|
||||
*/
|
||||
public function onProfileUpdate( $member, $changes )
|
||||
{
|
||||
if( \in_array( [ 'member_group_id', 'mgroup_others', 'temp_ban' ], array_keys( $changes ) ) )
|
||||
{
|
||||
$this->_updateHive( $member, 'update' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Member is merged with another member
|
||||
*
|
||||
* @param Member $member Member being kept
|
||||
* @param Member $member2 Member being removed
|
||||
* @return void
|
||||
*/
|
||||
public function onMerge( $member, $member2 )
|
||||
{
|
||||
$this->_updateHive( $member2, 'merge', $member->member_id );
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_hive_subscribers', [ 'member_id' => $member->member_id, 'subscribe_date' => time() ] );
|
||||
}
|
||||
catch( Db\Exception $e ) {}
|
||||
Db::i()->delete( 'core_hive_subscribers', [ 'member_id=?', $member2->member_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Member is deleted
|
||||
*
|
||||
* @param $member Member The member
|
||||
* @return void
|
||||
*/
|
||||
public function onDelete( $member )
|
||||
{
|
||||
$this->_updateHive( $member, 'delete' );
|
||||
Db::i()->delete( 'core_hive_subscribers', [ 'member_id=?', $member->member_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update HIVE API
|
||||
*
|
||||
* @param Member $member
|
||||
* @param string $type
|
||||
* @param int|null $newMergeId
|
||||
* @return void
|
||||
*/
|
||||
protected function _updateHive( Member $member, string $type, int $newMergeId=null )
|
||||
{
|
||||
/* Check to see if we might be subscribed before pushing this change */
|
||||
try
|
||||
{
|
||||
Db::i()->select( '*', 'core_hive_subscribers', [ 'member_id=?', $member->member_id ] )->first();
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'site_member_id' => $member->member_id,
|
||||
'group_hash' => $type == 'delete' ? 'guest' : Hive::groupHash( $member ),
|
||||
];
|
||||
|
||||
if( $type === 'delete' )
|
||||
{
|
||||
$data['new_site_member_id'] = 0;
|
||||
}
|
||||
elseif( $type === 'merge' AND $newMergeId )
|
||||
{
|
||||
$data['new_site_member_id'] = $newMergeId;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Hive::api( 'groupupdate', $data );
|
||||
}
|
||||
catch( \Exception | \Throwable | \LogicException $e ) { }
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
namespace IPS\core\extensions\core\MemberSync;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -167,6 +168,7 @@ class _System
|
||||
\IPS\Db::i()->delete( 'core_acp_notifications_preferences', array( '`member`=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_marketplace_tokens', array( 'id=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_achievements_log_milestones', array( 'milestone_member_id=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_member_privacy_actions', ['member_id=?', $member2->member_id] );
|
||||
|
||||
/* Update PM recipient counts */
|
||||
foreach( \IPS\Db::i()->select( 'map_topic_id', 'core_message_topic_user_map', array( 'map_user_id=?', $member->member_id ) ) as $topicId )
|
||||
@@ -246,6 +248,8 @@ class _System
|
||||
/* Rebuild permission array */
|
||||
$member->rebuildPermissionArray();
|
||||
|
||||
\IPS\Member\PrivacyAction::resetAcpNotificationCounts();
|
||||
|
||||
\IPS\Api\Webhook::fire( 'member_merged', ['kept' => $member->apiOutput(), 'removed' => $member2->apiOutput()] );
|
||||
}
|
||||
|
||||
@@ -329,7 +333,8 @@ class _System
|
||||
array( 'core_achievements_log_milestones', 'milestone_member_id' ),
|
||||
array( 'core_members_logins', 'member_id' ),
|
||||
array( 'core_stream_subscriptions', 'member_id' ),
|
||||
array( 'core_alerts_seen', 'seen_member_id' )
|
||||
array( 'core_alerts_seen', 'seen_member_id' ),
|
||||
array( 'core_member_privacy_actions', 'member_id' ),
|
||||
) as $toDelete )
|
||||
{
|
||||
\IPS\Db::i()->delete( $toDelete[0], array( '`' . $toDelete[1] . '`=?', $member->member_id ) );
|
||||
@@ -373,6 +378,9 @@ class _System
|
||||
$member->deletePhoto();
|
||||
$member->coverPhoto()->delete();
|
||||
|
||||
|
||||
\IPS\Member\PrivacyAction::resetAcpNotificationCounts();
|
||||
|
||||
\IPS\Api\Webhook::fire( 'member_delete', $member, $member->webhookFilters() );
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class _Achievements
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
// Return an array defining each notification type, including the form elements necessary to allow its configuration
|
||||
if ( \IPS\Settings::i()->achievements_enabled )
|
||||
|
||||
@@ -28,7 +28,7 @@ class _BulkMails
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
return array(
|
||||
'allow_admin_mails' => array(
|
||||
|
||||
@@ -28,7 +28,7 @@ class _Clubs
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$return = array();
|
||||
if ( \IPS\Settings::i()->clubs and $module = \IPS\Application\Module::get( 'core', 'clubs', 'front' ) and $module->_enabled and ( $member === NULL or $member->canAccessModule( $module ) ) )
|
||||
|
||||
@@ -28,7 +28,7 @@ class _Content
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$autoFollow = array();
|
||||
if ( $member )
|
||||
|
||||
@@ -28,7 +28,7 @@ class _Messenger
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class _Moderation
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class _MyStuff
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
return array(
|
||||
'core_MyStuff' => array(
|
||||
@@ -562,7 +562,7 @@ class _MyStuff
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__minesolved', FALSE, array(
|
||||
( $htmlEscape ? 'sprintf' : 'htmlsprintf' ) => array(
|
||||
$name,
|
||||
mb_strtolower( $item->indefiniteArticle() ),
|
||||
mb_strtolower( $item->definiteArticle() ),
|
||||
$item->mapped('title')
|
||||
)
|
||||
) ),
|
||||
@@ -594,7 +594,7 @@ class _MyStuff
|
||||
'body' => $language->addToStack( 'notification__minesolved', FALSE, array(
|
||||
'htmlsprintf' => array(
|
||||
$name,
|
||||
mb_strtolower( $item->indefiniteArticle() ),
|
||||
mb_strtolower( $item->definiteArticle() ),
|
||||
$item->mapped('title')
|
||||
)
|
||||
) ),
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Notification Options
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 08 Mar 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Notifications;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Options
|
||||
*/
|
||||
class _PiiDataRequest
|
||||
{
|
||||
/**
|
||||
* Get fields for configuration
|
||||
*
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$return = [];
|
||||
/* WE don't need any options in the frontend for this, they have just to accept it;) */
|
||||
if( $member === NULL )
|
||||
{
|
||||
$return['pii_data'] = [
|
||||
'type' => 'standard',
|
||||
'notificationTypes' => [ 'pii_data' ],
|
||||
'title' => 'notifications__core_pii_data_request',
|
||||
'showTitle' => TRUE,
|
||||
'description' => 'notifications__core_pii_data_request_desc',
|
||||
'default' => [ 'email','inline' ],
|
||||
'disabled' => [],
|
||||
'extra' => []
|
||||
];
|
||||
$return['pii_data_rejected'] = [
|
||||
'type' => 'standard',
|
||||
'notificationTypes' => [ 'pii_data_rejected' ],
|
||||
'title' => 'notifications__core_pii_data_request_rejected',
|
||||
'showTitle' => TRUE,
|
||||
'description' => 'notifications__core_pii_data_request_rejected_desc',
|
||||
'default' => [ 'email','inline' ],
|
||||
'disabled' => [],
|
||||
'extra' => []
|
||||
];
|
||||
|
||||
$return['account_del_request_rejected'] = [
|
||||
'type' => 'standard',
|
||||
'notificationTypes' => [ 'account_del_request_rejected' ],
|
||||
'title' => 'notifications__core_account_deletion_rejected',
|
||||
'showTitle' => TRUE,
|
||||
'description' => 'notifications__core_account_deletion_rejected__desc',
|
||||
'default' => [ 'email','inline' ],
|
||||
'disabled' => [],
|
||||
'extra' => []
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save "extra" value
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member or NULL if this is the admin setting defaults
|
||||
* @param string $key The key
|
||||
* @param bool $value The value
|
||||
* @return void
|
||||
*/
|
||||
public static function saveExtra( ?\IPS\Member $member, $key, $value )
|
||||
{
|
||||
// You can perform extra processing here to save values in a specific manner if needed
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable all "extra" values for a particular type
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member or NULL if this is the admin setting defaults
|
||||
* @param string $method The method type
|
||||
* @return void
|
||||
*/
|
||||
public static function disableExtra( ?\IPS\Member $member, $method )
|
||||
{
|
||||
// You can disable extra values for a given type, such as if a setting means the notification preference is irrelevant
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset "extra" value to the default for all accounts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetExtra()
|
||||
{
|
||||
// Any extra processing you may need to perform when resetting all members to default notification options.
|
||||
// Typically you won't have to do anything here unless you have custom settings
|
||||
}
|
||||
|
||||
// For each type of notification you need a method like this which controls what will be displayed when the user clicks on the notification icon in the header:
|
||||
// Note that for each type of notification you must *also* create email templates. See documentation for details: https://remoteservices.invisionpower.com/docs/devdocs-notifications
|
||||
|
||||
/**
|
||||
* Parse notification: key
|
||||
*
|
||||
* @param \IPS\Notification\Inline $notification The notification
|
||||
* @param bool $htmlEscape TRUE to escape HTML in title
|
||||
* @return array
|
||||
* @code
|
||||
return array(
|
||||
'title' => "Mark has replied to A Topic", // The notification title
|
||||
'url' => \IPS\Http\Url::internal( ... ), // The URL the notification should link to
|
||||
'content' => "Lorem ipsum dolar sit", // [Optional] Any appropriate content. Do not format this like an email where the text
|
||||
// explains what the notification is about - just include any appropriate content.
|
||||
// For example, if the notification is about a post, set this as the body of the post.
|
||||
'author' => \IPS\Member::load( 1 ), // [Optional] The user whose photo should be displayed for this notification
|
||||
);
|
||||
* @endcode
|
||||
*/
|
||||
public function parse_pii_data( \IPS\Notification\Inline $notification, $htmlEscape=TRUE ): array
|
||||
{
|
||||
return array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'pii_data_ready_notification'),
|
||||
'url' => \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse notification: key
|
||||
*
|
||||
* @param \IPS\Notification\Inline $notification The notification
|
||||
* @param bool $htmlEscape TRUE to escape HTML in title
|
||||
* @return array
|
||||
* @code
|
||||
return array(
|
||||
'title' => "Mark has replied to A Topic", // The notification title
|
||||
'url' => \IPS\Http\Url::internal( ... ), // The URL the notification should link to
|
||||
'content' => "Lorem ipsum dolar sit", // [Optional] Any appropriate content. Do not format this like an email where the text
|
||||
// explains what the notification is about - just include any appropriate content.
|
||||
// For example, if the notification is about a post, set this as the body of the post.
|
||||
'author' => \IPS\Member::load( 1 ), // [Optional] The user whose photo should be displayed for this notification
|
||||
);
|
||||
* @endcode
|
||||
*/
|
||||
public function parse_pii_data_rejected( \IPS\Notification\Inline $notification, $htmlEscape=TRUE ): array
|
||||
{
|
||||
return array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'pii_data_request_rejected'),
|
||||
'url' => \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse notification: key
|
||||
*
|
||||
* @param \IPS\Notification\Inline $notification The notification
|
||||
* @param bool $htmlEscape TRUE to escape HTML in title
|
||||
* @return array
|
||||
* @code
|
||||
return array(
|
||||
'title' => "Mark has replied to A Topic", // The notification title
|
||||
'url' => \IPS\Http\Url::internal( ... ), // The URL the notification should link to
|
||||
'content' => "Lorem ipsum dolar sit", // [Optional] Any appropriate content. Do not format this like an email where the text
|
||||
// explains what the notification is about - just include any appropriate content.
|
||||
// For example, if the notification is about a post, set this as the body of the post.
|
||||
'author' => \IPS\Member::load( 1 ), // [Optional] The user whose photo should be displayed for this notification
|
||||
);
|
||||
* @endcode
|
||||
*/
|
||||
public function parse_account_del_request_rejected( \IPS\Notification\Inline $notification, $htmlEscape=TRUE ): array
|
||||
{
|
||||
return array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'account_del_request_rejected'),
|
||||
'url' => \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class _Profile
|
||||
* @param \IPS\Member|null $member The member (to take out any notification types a given member will never see) or NULL if this is for the ACP
|
||||
* @return array
|
||||
*/
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
public static function configurationOptions( \IPS\Member $member = NULL ): array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
|
||||
@@ -36,7 +36,12 @@ class _Clubs
|
||||
*/
|
||||
public function getBlocks()
|
||||
{
|
||||
return array( 'clubs', 'joins' );
|
||||
if( \IPS\Settings::i()->clubs )
|
||||
{
|
||||
return array( 'clubs', 'joins' );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ class _FileCleanup
|
||||
public function preQueueData( $data )
|
||||
{
|
||||
/* Make sure we have the minimal amount of data we need */
|
||||
if( !isset( $data['table'] ) OR empty( $data['column'] ) OR empty( $data['storageExtension'] ) )
|
||||
if( !$this->canBeRun( $data ) )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
@@ -73,6 +73,11 @@ class _FileCleanup
|
||||
*/
|
||||
public function run( &$data, $offset )
|
||||
{
|
||||
if( !$this->canBeRun( $data) )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
$last = ( isset( $data['primaryId'] ) ) ? NULL : $offset;
|
||||
$deleted = 0;
|
||||
|
||||
@@ -169,4 +174,15 @@ class _FileCleanup
|
||||
\IPS\Db::i()->delete( $data['table'], $data['where'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the task can be run
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
protected function canBeRun( array $data ): bool
|
||||
{
|
||||
return !( !isset( $data['table'] ) OR empty( $data['column'] ) OR empty( $data['storageExtension'] ) OR !\IPS\Db::i()->checkForTable( $data['table'] ) );
|
||||
}
|
||||
}
|
||||
@@ -134,12 +134,12 @@ class _MoveMembers
|
||||
*
|
||||
* @param string $select What to select
|
||||
* @param array $data Queue data
|
||||
* @param int $offset Offset to use (FALSE to not apply limit)
|
||||
* @param int $applyLimit Offset to use (FALSE to not apply limit)
|
||||
* @return \IPS\Db\Select
|
||||
*/
|
||||
protected function getQuery( $select, $data, $applyLimit=FALSE )
|
||||
{
|
||||
return \IPS\Db::i()->select( $select, 'core_members', $data['where'], 'core_members.member_id ASC', ( $offset !== FALSE ) ? array( $offset, \IPS\REBUILD_SLOW ) : array() )
|
||||
return \IPS\Db::i()->select( $select, 'core_members', $data['where'], 'core_members.member_id ASC', ( $applyLimit !== FALSE ) ? array( $applyLimit, \IPS\REBUILD_SLOW ) : array() )
|
||||
->join( 'core_pfields_content', 'core_members.member_id=core_pfields_content.member_id' )
|
||||
->join( array( 'core_validating', 'v' ), 'v.member_id=core_members.member_id')
|
||||
->join( array( 'core_admin_permission_rows', 'm' ), "m.row_id=core_members.member_id AND m.row_id_type='member'" )
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Background Task
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 25 Apr 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Queue;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Background Task
|
||||
*/
|
||||
class _RebuildKeywords
|
||||
{
|
||||
/**
|
||||
* @brief Number of content items to rebuild per cycle
|
||||
*/
|
||||
public $rebuild = \IPS\REBUILD_SLOW;
|
||||
|
||||
/**
|
||||
* Parse data before queuing
|
||||
*
|
||||
* @param array $data
|
||||
* @return array|NULL
|
||||
*/
|
||||
public function preQueueData( $data ): ?array
|
||||
{
|
||||
$classname = $data['class'];
|
||||
|
||||
try
|
||||
{
|
||||
$where = ( is_subclass_of( $classname, 'IPS\Content\Comment' ) ) ? array( $classname::commentWhere() ) : array();
|
||||
if ( \IPS\Settings::i()->stats_keywords_prune )
|
||||
{
|
||||
$where[] = array( $classname::$databasePrefix . $classname::$databaseColumnMap['date'] . ">?", \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_keywords_prune . 'D' ) )->getTimestamp() );
|
||||
}
|
||||
$data['count'] = $classname::db()->select( 'MAX(' . $classname::$databasePrefix . $classname::$databaseColumnId . ')', $classname::$databaseTable, $where )->first();
|
||||
$data['realCount'] = $classname::db()->select( 'COUNT(*)', $classname::$databaseTable, $where )->first();
|
||||
$data['runPid'] = $data['count'] + 1;
|
||||
}
|
||||
catch( \Exception $ex )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
if( $data['count'] == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$data['indexed'] = 0;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Background Task
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return int New offset
|
||||
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function run( &$data, $offset )
|
||||
{
|
||||
$classname = $data['class'];
|
||||
$exploded = explode( '\\', $classname );
|
||||
if ( !class_exists( $classname ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
$where = ( is_subclass_of( $classname, 'IPS\Content\Comment' ) ) ? ( \is_array( $classname::commentWhere() ) ? $classname::commentWhere() : array() ) : array();
|
||||
$where[] = array( $classname::$databasePrefix . $classname::$databaseColumnId . ' < ?', $data['runPid'] );
|
||||
if ( \IPS\Settings::i()->stats_keywords_prune )
|
||||
{
|
||||
$where[] = array( $classname::$databasePrefix . $classname::$databaseColumnMap['date'] . ">?", \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_keywords_prune . 'D' ) )->getTimestamp() );
|
||||
}
|
||||
$select = $classname::db()->select( '*', $classname::$databaseTable, $where, $classname::$databasePrefix . $classname::$databaseColumnId . ' DESC', array( 0, $this->rebuild ) );
|
||||
$iterator = new \IPS\Patterns\ActiveRecordIterator( $select, $classname );
|
||||
$last = NULL;
|
||||
|
||||
foreach( $iterator as $item )
|
||||
{
|
||||
$idColumn = $classname::$databaseColumnId;
|
||||
$title = NULL;
|
||||
if ( is_subclass_of( $classname, 'IPS\Content\Item' ) )
|
||||
{
|
||||
$title = $item->mapped('title');
|
||||
}
|
||||
else if ( isset( $item::$itemClass ) )
|
||||
{
|
||||
$itemClass = $item::$itemClass;
|
||||
if ( isset( $itemClass::$firstCommentRequired ) AND $item->isFirst() )
|
||||
{
|
||||
$title = $item->item()->mapped('title');
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$item->checkKeywords( $item->content(), $title, ( isset( $classname::$databaseColumnMap['date'] ) ) ? $item->mapped('date') : NULL );
|
||||
}
|
||||
catch( \Throwable $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'keyword_rebuild' );
|
||||
}
|
||||
|
||||
$last = $item->$idColumn;
|
||||
|
||||
$data['indexed']++;
|
||||
}
|
||||
|
||||
$data['runPid'] = $last;
|
||||
|
||||
if( $last === NULL )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
return $data['indexed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Progress
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
|
||||
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function getProgress( $data, $offset )
|
||||
{
|
||||
$class = $data['class'];
|
||||
$exploded = explode( '\\', $class );
|
||||
if ( !class_exists( $class ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('rebuilding_keywords', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl_lc' ) ) ) ), 'complete' => $data['realCount'] ? ( round( 100 / $data['realCount'] * $data['indexed'], 2 ) ) : 100 );
|
||||
}
|
||||
}
|
||||
@@ -133,29 +133,42 @@ class _RebuildUrlRels
|
||||
this on the next load and skip the problematic content */
|
||||
\IPS\Data\Store::i()->currentUrlRefRebuild = array( $classname, $item->$idColumn );
|
||||
|
||||
/* Only run an update if the content has actually changed */
|
||||
if( $newContent = \IPS\Text\Parser::rebuildUrlRels( $item->$contentColumn, $item->author() ) AND $newContent != $item->$contentColumn )
|
||||
/* Test to see if the author object returns correctly */
|
||||
try
|
||||
{
|
||||
$item->$contentColumn = $newContent;
|
||||
$author = $item->author();
|
||||
}
|
||||
catch( \Exception )
|
||||
{
|
||||
$author = false;
|
||||
}
|
||||
|
||||
try
|
||||
/* Only run an update if the content has actually changed */
|
||||
if ( $author )
|
||||
{
|
||||
if( $newContent = \IPS\Text\Parser::rebuildUrlRels( $item->$contentColumn,$author ) AND $newContent != $item->$contentColumn )
|
||||
{
|
||||
$item->save();
|
||||
}
|
||||
/* Content item may be orphaned, continue if we cannot save it. */
|
||||
catch( \OutOfRangeException $e ) {}
|
||||
/* Status updates could cause an error with legacy data, which we'll need to ignore */
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
/* If this is not a "data too long for column" error we want to throw the exception regardless of what type of content */
|
||||
if( $e->getCode() != 1406 )
|
||||
$item->$contentColumn = $newContent;
|
||||
|
||||
try
|
||||
{
|
||||
throw $e;
|
||||
$item->save();
|
||||
}
|
||||
/* We are only hiding this error for status updates and replies */
|
||||
elseif( !( $item instanceof \IPS\core\Statuses\Status OR $item instanceof \IPS\core\Statuses\Reply ) )
|
||||
/* Content item may be orphaned, continue if we cannot save it. */
|
||||
catch( \OutOfRangeException $e ) {}
|
||||
/* Status updates could cause an error with legacy data, which we'll need to ignore */
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
throw $e;
|
||||
/* If this is not a "data too long for column" error we want to throw the exception regardless of what type of content */
|
||||
if( $e->getCode() != 1406 )
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
/* We are only hiding this error for status updates and replies */
|
||||
elseif( !( $item instanceof \IPS\core\Statuses\Status OR $item instanceof \IPS\core\Statuses\Reply ) )
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Background Task
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 13 Apr 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Queue;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Background Task
|
||||
*/
|
||||
class _UpdateClubRenewals
|
||||
{
|
||||
/**
|
||||
* Parse data before queuing
|
||||
*
|
||||
* @param array $data
|
||||
* @return array|null
|
||||
*/
|
||||
public function preQueueData( $data ): array|null
|
||||
{
|
||||
$data['count'] = $this->getQuery( 'COUNT(*)', $data )->first();
|
||||
|
||||
if( $data['count'] == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Background Task
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return int New offset
|
||||
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function run( $data, $offset )
|
||||
{
|
||||
if ( !\IPS\Application::appisEnabled( 'nexus' ) )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
$select = $this->getQuery( 'nexus_purchases.*', $data, $offset );
|
||||
|
||||
if ( !$select->count() or $offset > $data['count'] )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
foreach( $select AS $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
$club = \IPS\Member\Club::load( $data['club'] );
|
||||
$purchase = \IPS\nexus\Purchase::constructFromData( $row );
|
||||
|
||||
$club->updatePurchase( $purchase, $data['changes'], TRUE );
|
||||
}
|
||||
catch( \Exception $e ) {}
|
||||
|
||||
$offset++;
|
||||
}
|
||||
|
||||
return $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Progress
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
|
||||
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function getProgress( $data, $offset ): array
|
||||
{
|
||||
$text = \IPS\Member::loggedIn()->language()->addToStack('updating_club_renewals', FALSE, array() );
|
||||
|
||||
return array( 'text' => $text, 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $offset, 2 ) ) : 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query
|
||||
*
|
||||
* @param string $select What to select
|
||||
* @param array $data Queue data
|
||||
* @param int $offset Offset to use (FALSE to not apply limit)
|
||||
* @return \IPS\Db\Select
|
||||
*/
|
||||
protected function getQuery( $select, $data, $offset=FALSE )
|
||||
{
|
||||
return \IPS\Db::i()->select( $select, 'nexus_purchases', array( "ps_app=? and ps_type=? and ps_item_id=?", 'core', 'club', $data['club'] ), 'ps_id', ( $offset !== FALSE ) ? array( $offset, \IPS\REBUILD_QUICK ) : array() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Badges extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_badges_type';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_member_badges', 'datetime', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array('gridlines' => array('color' => '#f5f5f5')),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'AreaChart',
|
||||
'daily',
|
||||
array('start' => 0, 'end' => 0),
|
||||
array(),
|
||||
'type'
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_badges_title' );
|
||||
$chart->availableTypes = array('AreaChart', 'ColumnChart', 'BarChart');
|
||||
$chart->enableHourly = FALSE;
|
||||
|
||||
$chart->groupBy = 'badge';
|
||||
|
||||
foreach ( \IPS\core\Achievements\Badge::roots() as $badge )
|
||||
{
|
||||
$chart->addSeries( $badge->_title, 'number', 'COUNT(*)', TRUE, $badge->id );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_badges_title' );
|
||||
$chart->availableTypes = array('AreaChart', 'ColumnChart', 'BarChart');
|
||||
$chart->showIntervals = TRUE;
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _CommunityActivity extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_communityactivity';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Callback(
|
||||
$url,
|
||||
array( $this, 'getResults' ),
|
||||
'',
|
||||
array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'AreaChart',
|
||||
'weekly',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P90D' ) ), 'end' => \IPS\DateTime::ts( time() ) )
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('activity'), 'number', FALSE );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('member_activity_timeperiod');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the results
|
||||
*
|
||||
* @param \IPS\Helpers\Chart\Callback $chart Chart object
|
||||
* @return array
|
||||
*/
|
||||
public function getResults( $chart )
|
||||
{
|
||||
/* Get results */
|
||||
$results = array();
|
||||
|
||||
if ( \IPS\Settings::i()->search_method == 'mysql' )
|
||||
{
|
||||
$results = array_replace_recursive( $results, $this->getSqlResults( 'core_search_index', 'index_date_updated', 'index_author', $chart ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
|
||||
{
|
||||
foreach ( $contentRouter->classes as $class )
|
||||
{
|
||||
if ( isset( $class::$databaseColumnMap['author'] ) )
|
||||
{
|
||||
$results = array_replace_recursive( $results, $this->getSqlResults(
|
||||
$class::$databaseTable,
|
||||
$class::$databasePrefix . ( isset( $class::$databaseColumnMap['updated'] ) ? $class::$databaseColumnMap['updated'] : $class::$databaseColumnMap['date'] ),
|
||||
$class::$databasePrefix . $class::$databaseColumnMap['author'],
|
||||
$chart
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$results = array_replace_recursive( $results, $this->getSqlResults( 'core_reputation_index', 'rep_date', 'member_id', $chart ) );
|
||||
$results = array_replace_recursive( $results, $this->getSqlResults( 'core_follow', 'follow_added', 'follow_member_id', $chart ) );
|
||||
|
||||
/* Reformat now */
|
||||
$finalResults = array();
|
||||
|
||||
foreach( $results as $date => $members )
|
||||
{
|
||||
$finalResults[ $date ] = array( 'time' => $date, \IPS\Member::loggedIn()->language()->get('activity') => \count( $members ) );
|
||||
}
|
||||
|
||||
return $finalResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL query/results
|
||||
*
|
||||
* @note Consolidated to reduce duplicated code
|
||||
* @param string $table Database table
|
||||
* @param string $date Date column
|
||||
* @param string $author Author column
|
||||
* @param object $chart Chart
|
||||
* @return array
|
||||
*/
|
||||
protected function getSqlResults( $table, $date, $author, $chart )
|
||||
{
|
||||
/* What's our SQL time? */
|
||||
switch ( $chart->timescale )
|
||||
{
|
||||
case 'daily':
|
||||
$timescale = '%Y-%c-%e';
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
$timescale = '%x-%v';
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
$timescale = '%Y-%c';
|
||||
break;
|
||||
}
|
||||
|
||||
$results = array();
|
||||
$where = array( array( "{$date}>?", 0 ) );
|
||||
$where[] = array( "{$author}>?", 0 );
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "{$date}>?", $chart->start->getTimestamp() );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "{$date}<?", $chart->end->getTimestamp() );
|
||||
}
|
||||
|
||||
/* First we need to get search index activity */
|
||||
$fromUnixTime = "FROM_UNIXTIME( IFNULL( {$date}, 0 ) )";
|
||||
if ( !$chart->timezoneError and \IPS\Member::loggedIn()->timezone and \in_array( \IPS\Member::loggedIn()->timezone, \IPS\DateTime::getTimezoneIdentifiers() ) )
|
||||
{
|
||||
$fromUnixTime = "CONVERT_TZ( {$fromUnixTime}, @@session.time_zone, '" . \IPS\Db::i()->escape_string( \IPS\Member::loggedIn()->timezone ) . "' )";
|
||||
}
|
||||
|
||||
$stmt = \IPS\Db::i()->select( "DATE_FORMAT( {$fromUnixTime}, '{$timescale}' ) AS time, {$author}", $table, $where, 'time ASC', NULL, array( $author, 'time' ) );
|
||||
|
||||
foreach( $stmt as $row )
|
||||
{
|
||||
$results[ $row['time'] ][ $row[ $author ] ] = 1;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Conversations extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_messengerstats_pmstats';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_message_posts', 'msg_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
) );
|
||||
$chart->setExtension( $this );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('new_conversations'), 'number', 'SUM(msg_is_first_post)' );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('mt_replies'), 'number', '( count(*) - SUM(msg_is_first_post) )' );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_messages_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _DeletedContent extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_deletedcontent';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_deletion_log', 'dellog_deleted_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'dellog_content_class', 'dellog_deleted_date' ), 'deletedcontent' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'dellog_content_class';
|
||||
|
||||
$types = \IPS\Db::i()->select( 'DISTINCT(dellog_content_class)', 'core_deletion_log' );
|
||||
|
||||
foreach( $types as $class )
|
||||
{
|
||||
$lang = $class::$title;
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack( $lang ), 'number', 'COUNT(*)', TRUE, $class );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_deletedcontent_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'dellog_deleted_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _DeviceUsage extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_deviceusage';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->stats_device_usage_prune )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_device_usage_prune . 'D' ) );
|
||||
}
|
||||
|
||||
/* We can't retrieve any stats prior to the new tracking being implemented */
|
||||
try
|
||||
{
|
||||
$oldestLog = \IPS\Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'online_users' ) )->first();
|
||||
|
||||
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::ts( $oldestLog );
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
/* We have nothing tracked, set minimum date to today */
|
||||
$minimumDate = \IPS\DateTime::create();
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_statistics', 'time', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'AreaChart', 'hourly' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->where[] = array( 'type=?', 'devices' );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_deviceusage_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->enableHourly = TRUE;
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_devices_mobiles'), 'number', 'SUM(value_1)', TRUE );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_devices_tablets'), 'number', 'SUM(value_2)', TRUE );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_devices_consoles'), 'number', 'SUM(value_3)', TRUE );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_devices_desktops'), 'number', 'SUM(value_4)', TRUE );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the results
|
||||
*
|
||||
* @param \IPS\Helpers\Chart\Callback $chart Chart object
|
||||
* @return array
|
||||
*/
|
||||
public function getResults( $chart )
|
||||
{
|
||||
$where = array( array( 'type=?', 'online_users' ), array( "time>?", 0 ) );
|
||||
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "time>?", $chart->start->getTimestamp() );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "time<?", $chart->end->getTimestamp() );
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_statistics', $where, 'time ASC' ) as $row )
|
||||
{
|
||||
if( !isset( $results[ $row['time'] ] ) )
|
||||
{
|
||||
$results[ $row['time'] ] = array(
|
||||
'time' => $row['time'],
|
||||
\IPS\Member::loggedIn()->language()->get('members') => 0,
|
||||
\IPS\Member::loggedIn()->language()->get('guests') => 0
|
||||
);
|
||||
}
|
||||
|
||||
if( $row['value_4'] == 'members' )
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get('members') ] = $row['value_1'];
|
||||
}
|
||||
|
||||
if( $row['value_4'] == 'guests' )
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get('guests') ] = $row['value_1'];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _EmailClicks extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_emailstats_clicks';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->prune_log_emailstats > 0 )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->prune_log_emailstats . 'D' ) );
|
||||
}
|
||||
|
||||
/* We can't retrieve any stats prior to the new tracking being implemented */
|
||||
try
|
||||
{
|
||||
$oldestLog = \IPS\Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'emails_sent' ) )->first();
|
||||
|
||||
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::ts( $oldestLog );
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
/* We have nothing tracked, set minimum date to today */
|
||||
$minimumDate = \IPS\DateTime::create();
|
||||
}
|
||||
|
||||
$startDate = \IPS\DateTime::ts( time() - ( 60 * 60 * 24 * 30 ) );
|
||||
|
||||
/* If our start date is older than our minimum date, use that as the start date instead */
|
||||
if ( $startDate->getTimestamp() < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$startDate = $minimumDate;
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Callback(
|
||||
$url,
|
||||
array( $this, 'getResults' ),
|
||||
'',
|
||||
array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4,
|
||||
'height' => 450
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
array( 'start' => $startDate, 'end' => \IPS\DateTime::create() ),
|
||||
'',
|
||||
$minimumDate
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->availableTypes = array( 'LineChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_emailstats_clicks');
|
||||
|
||||
/* Force the notice that the chart is displayed in the server time zone */
|
||||
$chart->timezoneError = TRUE;
|
||||
$chart->hideTimezoneLink = TRUE;
|
||||
|
||||
foreach( $this->_getEmailTypes() as $series )
|
||||
{
|
||||
$chart->addSeries( $series, 'number' );
|
||||
}
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the results
|
||||
*
|
||||
* @param \IPS\Helpers\Chart\Callback $chart Chart object
|
||||
* @return array
|
||||
*/
|
||||
public function getResults( $chart )
|
||||
{
|
||||
$where = array( array( "time>?", 0 ) );
|
||||
$where[] = array( 'type=?', 'email_clicks' );
|
||||
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "value_4>=?", $chart->start->format('Y-m-d') );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "value_4<=?", $chart->end->format('Y-m-d') );
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_statistics', $where, 'value_4 ASC' ) as $row )
|
||||
{
|
||||
/* We need to use month/days NOT prefixed with '0' - i.e. 2019-2-12 instead of 2019-02-12 - to match the chart helper */
|
||||
$_date = new \IPS\DateTime( $row['value_4'] );
|
||||
|
||||
switch( $chart->timescale )
|
||||
{
|
||||
case 'daily':
|
||||
$_date = $_date->format( 'Y-n-j' );
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
$_date = $_date->format( 'o-W' );
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
$_date = $_date->format( 'Y-n' );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if( !isset( $results[ $_date ] ) )
|
||||
{
|
||||
$results[ $_date ] = array( 'time' => $_date );
|
||||
|
||||
foreach( $this->_getEmailTypes() as $series )
|
||||
{
|
||||
$results[ $_date ][ $series ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$lang = $row['extra_data'];
|
||||
|
||||
try
|
||||
{
|
||||
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $row['extra_data'] );
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
$results[ $_date ][ $lang ] += (int) $row['value_1'];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached email types
|
||||
*/
|
||||
protected $_emailTypes = NULL;
|
||||
|
||||
/**
|
||||
* Get all possible email types logged
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getEmailTypes()
|
||||
{
|
||||
if( $this->_emailTypes === NULL )
|
||||
{
|
||||
$this->_emailTypes = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'extra_data', 'core_statistics', array( 'type=?', 'emails_sent' ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT ) as $series )
|
||||
{
|
||||
$lang = $series;
|
||||
|
||||
try
|
||||
{
|
||||
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $series );
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
$this->_emailTypes[] = $lang;
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique( $this->_emailTypes );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Emails extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_emailstats_emails';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->prune_log_emailstats > 0 )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->prune_log_emailstats . 'D' ) );
|
||||
}
|
||||
|
||||
/* We can't retrieve any stats prior to the new tracking being implemented */
|
||||
try
|
||||
{
|
||||
$oldestLog = \IPS\Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'emails_sent' ) )->first();
|
||||
|
||||
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::ts( $oldestLog );
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
/* We have nothing tracked, set minimum date to today */
|
||||
$minimumDate = \IPS\DateTime::create();
|
||||
}
|
||||
|
||||
$startDate = \IPS\DateTime::ts( time() - ( 60 * 60 * 24 * 30 ) );
|
||||
|
||||
/* If our start date is older than our minimum date, use that as the start date instead */
|
||||
if ( $startDate->getTimestamp() < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$startDate = $minimumDate;
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Callback(
|
||||
$url,
|
||||
array( $this, 'getResults' ),
|
||||
'',
|
||||
array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4,
|
||||
'height' => 450
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
array( 'start' => $startDate, 'end' => \IPS\DateTime::create() ),
|
||||
'',
|
||||
$minimumDate
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->availableTypes = array( 'LineChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_emailstats_emails');
|
||||
|
||||
/* Force the notice that the chart is displayed in the server time zone */
|
||||
$chart->timezoneError = TRUE;
|
||||
$chart->hideTimezoneLink = TRUE;
|
||||
|
||||
foreach( $this->_getEmailTypes() as $series )
|
||||
{
|
||||
$chart->addSeries( $series, 'number' );
|
||||
}
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the results
|
||||
*
|
||||
* @param \IPS\Helpers\Chart\Callback $chart Chart object
|
||||
* @return array
|
||||
*/
|
||||
public function getResults( $chart )
|
||||
{
|
||||
$where = array( array( "time>?", 0 ) );
|
||||
$where[] = array( 'type=?', 'emails_sent' );
|
||||
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "value_4>=?", $chart->start->format('Y-m-d') );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "value_4<=?", $chart->end->format('Y-m-d') );
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_statistics', $where, 'value_4 ASC' ) as $row )
|
||||
{
|
||||
/* We need to use month/days NOT prefixed with '0' - i.e. 2019-2-12 instead of 2019-02-12 - to match the chart helper */
|
||||
$_date = new \IPS\DateTime( $row['value_4'] );
|
||||
|
||||
switch( $chart->timescale )
|
||||
{
|
||||
case 'daily':
|
||||
$_date = $_date->format( 'Y-n-j' );
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
$_date = $_date->format( 'o-W' );
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
$_date = $_date->format( 'Y-n' );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if( !isset( $results[ $_date ] ) )
|
||||
{
|
||||
$results[ $_date ] = array( 'time' => $_date );
|
||||
|
||||
foreach( $this->_getEmailTypes() as $series )
|
||||
{
|
||||
$results[ $_date ][ $series ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$lang = $row['extra_data'];
|
||||
|
||||
try
|
||||
{
|
||||
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $row['extra_data'] );
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
$results[ $_date ][ $lang ] += (int) $row['value_1'];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached email types
|
||||
*/
|
||||
protected $_emailTypes = NULL;
|
||||
|
||||
/**
|
||||
* Get all possible email types logged
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getEmailTypes()
|
||||
{
|
||||
if( $this->_emailTypes === NULL )
|
||||
{
|
||||
$this->_emailTypes = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'extra_data', 'core_statistics', array( 'type=?', 'emails_sent' ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT ) as $series )
|
||||
{
|
||||
$lang = $series;
|
||||
|
||||
try
|
||||
{
|
||||
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $series );
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
$this->_emailTypes[] = $lang;
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique( $this->_emailTypes );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Keywords extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_keywords';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->stats_keywords_prune )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_keywords_prune . 'D' ) );
|
||||
}
|
||||
|
||||
/* Draw a chart */
|
||||
$options = json_decode( \IPS\Settings::i()->stats_keywords, true );
|
||||
|
||||
if( !\is_array( $options ) )
|
||||
{
|
||||
$options = array();
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Database(
|
||||
$url,
|
||||
'core_statistics',
|
||||
'time',
|
||||
'',
|
||||
array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde', '#b09be4', '#eec766', '#9fc973', '#e291bf', '#55c1a6', '#5fb9da' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P90D' ) ), 'end' => \IPS\DateTime::ts( time() ) ),
|
||||
array(),
|
||||
'',
|
||||
$minimumDate
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
$chart->where = array( array( 'type=?', 'keyword' ) );
|
||||
$chart->groupBy = 'value_4';
|
||||
|
||||
if ( \is_array( $options ) )
|
||||
{
|
||||
foreach( $options as $k => $v )
|
||||
{
|
||||
$chart->addSeries( $v, 'number', 'COUNT(*)' );
|
||||
}
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('keyword_usage_chart');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Language extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_preferences_theme';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart;
|
||||
$counts = iterator_to_array( \IPS\Db::i()->select( 'language, COUNT(member_id) as count', 'core_members', array( "language > ?", 0), NULL, NULL, "language" )->setKeyField( 'language' ) );
|
||||
|
||||
$chart->addHeader( "language", "string" );
|
||||
$chart->addHeader( \IPS\Member::loggedIn()->language()->get('chart_members'), "number" );
|
||||
|
||||
/* We need to make sure the language exists - otherwise apply the count to the default language. */
|
||||
$rows = [];
|
||||
foreach( $counts as $id => $lang )
|
||||
{
|
||||
try
|
||||
{
|
||||
$l = \IPS\Lang::load( $id );
|
||||
if ( !isset( $rows[ $id ] ) )
|
||||
{
|
||||
$rows[ $id ] = array( 'title' => $l->title, 'count' => 0 );
|
||||
}
|
||||
$rows[ $id ]['count'] += $lang['count'];
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
if ( !isset( $rows[ \IPS\Lang::defaultLanguage() ] ) )
|
||||
{
|
||||
$rows[ \IPS\Lang::defaultLanguage() ] = array( 'title' => \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->title, 'count' => 0 );
|
||||
}
|
||||
$rows[ \IPS\Lang::defaultLanguage() ]['count'] += $lang['count'];
|
||||
}
|
||||
}
|
||||
|
||||
/* Now add the rows to the chart */
|
||||
foreach( $rows AS $row )
|
||||
{
|
||||
$chart->addRow( array( $row['title'], $row['count'] ) );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_language_title');
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Moderators extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_moderators';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_moderator_logs', 'ctime', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'member_id', 'ctime' ), 'spam' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'member_id';
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_moderator_activity_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'ctime' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
/* Didn't find any specified? Do the default of the top 50 moderators */
|
||||
$where = array();
|
||||
if ( $chart->start instanceof \IPS\DateTime or $chart->end instanceof \IPS\DateTime )
|
||||
{
|
||||
$start = $chart->start instanceof \IPS\DateTime ? $chart->start->getTimestamp() : 0;
|
||||
$end = $chart->end instanceof \IPS\DateTime ? $chart->end->getTimestamp() : time();
|
||||
$where[] = array( 'ctime BETWEEN ? AND ?', $start, $end );
|
||||
}
|
||||
|
||||
/* Get actual moderators */
|
||||
$modMember = [];
|
||||
$modGroup = [];
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $mod )
|
||||
{
|
||||
if ( $mod['type'] == 'g' )
|
||||
{
|
||||
$modGroup[] = $mod['id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$modMember[] = $mod['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$query = [];
|
||||
|
||||
if ( \count( $modGroup ) )
|
||||
{
|
||||
$query[] = \IPS\Db::i()->in( 'member_group_id', $modGroup );
|
||||
$query[] = \IPS\Db::i()->findInSet( 'mgroup_others', $modGroup );
|
||||
}
|
||||
|
||||
if ( \count( $modMember ) )
|
||||
{
|
||||
$query[] = \IPS\Db::i()->in( 'member_id', $modMember );
|
||||
}
|
||||
|
||||
/* I mean $query should never be empty, but lets not wait for a ticket and have to release a patch to find out that it could be... */
|
||||
if ( \count( $query ) )
|
||||
{
|
||||
$where[] = [ 'member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', implode( ' OR ', $query ) ) ];
|
||||
}
|
||||
|
||||
$topModerators = \IPS\Db::i()->select( 'member_id, COUNT(*) as _mod_count', 'core_moderator_logs', $where, '_mod_count DESC', 50, array( 'member_id' ) );
|
||||
|
||||
foreach ( $topModerators as $moderatorRow )
|
||||
{
|
||||
$member = \IPS\Member::load( $moderatorRow['member_id'] );
|
||||
$chart->addSeries(
|
||||
( $member->member_id ) ? $member->name : \IPS\Member::loggedIn()->language()->addToStack( 'deleted_member' ),
|
||||
'number',
|
||||
'COUNT(*)',
|
||||
TRUE,
|
||||
$moderatorRow['member_id']
|
||||
);
|
||||
}
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _OnlineUsers extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_onlineusers';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->stats_online_users_prune )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_online_users_prune . 'D' ) );
|
||||
}
|
||||
|
||||
/* We can't retrieve any stats prior to the new tracking being implemented */
|
||||
try
|
||||
{
|
||||
$oldestLog = \IPS\Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'online_users' ) )->first();
|
||||
|
||||
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::ts( $oldestLog );
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
/* We have nothing tracked, set minimum date to today */
|
||||
$minimumDate = \IPS\DateTime::create();
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Callback(
|
||||
$url,
|
||||
array( $this, 'getResults' ),
|
||||
'',
|
||||
array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'AreaChart',
|
||||
'none',
|
||||
array( 'start' => \IPS\DateTime::ts( time() - ( 60 * 60 * 24 * 30 ) ), 'end' => \IPS\DateTime::create() ),
|
||||
'',
|
||||
$minimumDate
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('members'), 'number' );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('guests'), 'number' );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_onlineusers_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->showIntervals = FALSE;
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the results
|
||||
*
|
||||
* @param \IPS\Helpers\Chart\Callback $chart Chart object
|
||||
* @return array
|
||||
*/
|
||||
public function getResults( $chart )
|
||||
{
|
||||
$where = array( array( 'type=?', 'online_users' ), array( "time>?", 0 ) );
|
||||
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "time>?", $chart->start->getTimestamp() );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "time<?", $chart->end->getTimestamp() );
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_statistics', $where, 'time ASC' ) as $row )
|
||||
{
|
||||
if( !isset( $results[ $row['time'] ] ) )
|
||||
{
|
||||
$results[ $row['time'] ] = array(
|
||||
'time' => $row['time'],
|
||||
\IPS\Member::loggedIn()->language()->get('members') => 0,
|
||||
\IPS\Member::loggedIn()->language()->get('guests') => 0
|
||||
);
|
||||
}
|
||||
|
||||
if( $row['value_4'] == 'members' )
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get('members') ] = $row['value_1'];
|
||||
}
|
||||
|
||||
if( $row['value_4'] == 'guests' )
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get('guests') ] = $row['value_1'];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Points extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = NULL;
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=points' ), 'core_points_log', 'datetime', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'AreaChart', 'daily' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_points_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->enableHourly = FALSE;
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_member_points'), 'number', 'SUM(points)', TRUE );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_points_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->showIntervals = TRUE;
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _RankProgression extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_rankprogression';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart;
|
||||
$chart->addHeader( "Rank", 'string' );
|
||||
$chart->addHeader( "Days", 'number' );
|
||||
|
||||
$data = \IPS\Db::i()->select( 'new_rank, AVG(time_to_new_rank) as time_to_rank', 'core_points_log', array('time_to_new_rank IS NOT NULL'), 'core_member_ranks.points ASC', NULL, ['new_rank'] );
|
||||
$data->join( 'core_member_ranks', 'core_member_ranks.id = core_points_log.new_rank' );
|
||||
|
||||
foreach ( $data as $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
$rank = \IPS\core\Achievements\Rank::load( $row['new_rank'] );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$chart->addRow( array( $rank->_title, floor( $row['time_to_rank'] / 86400 ) ) );
|
||||
}
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Reactions extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_reactions_type';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_reputation_index', 'rep_date', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde', '#b09be4', '#eec766', '#9fc973', '#e291bf', '#55c1a6', '#5fb9da' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'PieChart',
|
||||
'monthly',
|
||||
array( 'start' => 0, 'end' => 0 ),
|
||||
array(),
|
||||
'type'
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'reaction';
|
||||
|
||||
foreach( \IPS\Content\Reaction::roots() as $reaction )
|
||||
{
|
||||
$chart->addSeries( $reaction->_title, 'number', 'COUNT(*)', TRUE, $reaction->id );
|
||||
}
|
||||
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart', 'PieChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _ReactionsApp extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_reactions_type';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_reputation_index', 'rep_date', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde', '#b09be4', '#eec766', '#9fc973', '#e291bf', '#55c1a6', '#5fb9da' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'PieChart',
|
||||
'monthly',
|
||||
array( 'start' => 0, 'end' => 0 ),
|
||||
array(),
|
||||
'app'
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'app';
|
||||
|
||||
foreach( \IPS\Application::roots() as $app )
|
||||
{
|
||||
$chart->addSeries( $app->_title, 'number', 'COUNT(*)', TRUE, $app->directory );
|
||||
}
|
||||
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart', 'PieChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 24 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Registrations extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_registrationstats';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_members', 'joined', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
) );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_new_registrations'), 'number', 'COUNT(*)', FALSE );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_registrations_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
/* fetch only successful registered members ; if this needs to be changed, please review the other areas where we have the name<>? AND email<>? condition */
|
||||
$chart->where[] = array( 'completed=?', true );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Reports extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_reports';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_rc_reports', 'date_reported', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
) );
|
||||
$chart->setExtension( $this );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_reports'), 'number', 'COUNT(*)', FALSE );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_reports_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Search extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_activitystats_search';
|
||||
|
||||
/**
|
||||
* @brief Default limit to number of graphed results
|
||||
*/
|
||||
protected $defaultLimit = 25;
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
if( \IPS\Settings::i()->stats_search_prune )
|
||||
{
|
||||
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_search_prune . 'D' ) );
|
||||
}
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Database(
|
||||
$url,
|
||||
'core_statistics',
|
||||
'time',
|
||||
'',
|
||||
array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde', '#b09be4', '#eec766', '#9fc973', '#e291bf', '#55c1a6', '#5fb9da' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4,
|
||||
'limitSearch' => 'stats_search_term_menu',
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P90D' ) ), 'end' => \IPS\DateTime::ts( time() ) ),
|
||||
array(),
|
||||
'',
|
||||
$minimumDate
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
$chart->where = array( array( 'type=?', 'search' ) );
|
||||
$chart->groupBy = 'value_4';
|
||||
|
||||
$terms = [];
|
||||
foreach( $this->getTerms( $chart->searchTerm ) as $v )
|
||||
{
|
||||
$terms[] = $v;
|
||||
$chart->addSeries( $v, 'number', 'COUNT(*)', FALSE );
|
||||
}
|
||||
|
||||
if ( \count( $terms ) )
|
||||
{
|
||||
$chart->where[] = [ \IPS\Db::i()->in( 'value_4', $terms ) ];
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('search_stats_chart');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached top search terms
|
||||
*/
|
||||
protected $_topSearchTerms = array();
|
||||
|
||||
/**
|
||||
* Get the top search terms
|
||||
*
|
||||
* @param string|null $term Term we searched for
|
||||
* @return array
|
||||
*/
|
||||
public function getTerms( $term=NULL )
|
||||
{
|
||||
if( !isset( $this->_topSearchTerms[ $term ] ) )
|
||||
{
|
||||
$this->_topSearchTerms[ $term ] = array();
|
||||
|
||||
$where = array( array( 'type=?', 'search' ) );
|
||||
|
||||
if( $term !== NULL )
|
||||
{
|
||||
$where[] = \IPS\Db::i()->like( 'value_4', $term, TRUE, TRUE, TRUE );
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'SQL_BIG_RESULT value_4, COUNT(*) as total', 'core_statistics', $where, 'total DESC', $this->defaultLimit, 'value_4' ) as $searchedValue )
|
||||
{
|
||||
$this->_topSearchTerms[ $term ][] = $searchedValue['value_4'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_topSearchTerms[ $term ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Spam extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_spam';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_spam_service_log', 'log_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'log_code', 'log_date' ), 'spam' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'log_code';
|
||||
|
||||
foreach( array( 1,2,3,4 ) as $v )
|
||||
{
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('spam_service_action_stats_' . $v ), 'number', 'COUNT(*)', TRUE, $v );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_spam_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'log_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Tags extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = NULL;
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_tags', 'tag_added', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'AreaChart',
|
||||
'daily',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P1M' ) ), 'end' => 0 )
|
||||
);
|
||||
$chart->setExtension( $this );
|
||||
$chart->groupBy = 'tag_text';
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart', 'PieChart' );
|
||||
|
||||
$where = $chart->where;
|
||||
$where[] = array( "tag_added>?", 0 );
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "tag_added>?", $chart->start->getTimestamp() );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "tag_added<?", $chart->end->getTimestamp() );
|
||||
}
|
||||
|
||||
/* Only get visible tags */
|
||||
$where[] = array( 'tag_aai_lookup IN(?)', \IPS\Db::i()->select( 'tag_perm_aai_lookup', 'core_tags_perms', [ 'tag_perm_visible=1' ] ) );
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'tag_text', 'core_tags', $where, NULL, NULL, array( 'tag_text' ) ) as $tag )
|
||||
{
|
||||
$chart->addSeries( $tag, 'number', 'COUNT(*)', TRUE, $tag );
|
||||
}
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 26 Jan 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _Theme extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_preferences_theme';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart;
|
||||
$counts = iterator_to_array( \IPS\Db::i()->select( 'skin, COUNT(member_id) as count', 'core_members', array( "skin > ?", 0), NULL, NULL, "skin" )->setKeyField( 'skin' ) );
|
||||
|
||||
$chart->addHeader( "theme", "string" );
|
||||
$chart->addHeader( \IPS\Member::loggedIn()->language()->get('chart_members'), "number" );
|
||||
foreach( $counts as $id => $theme )
|
||||
{
|
||||
$chart->addRow( array( \IPS\Member::loggedIn()->language()->addToStack('core_theme_set_title_' . $id ), $theme['count'] ) );
|
||||
}
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_theme_title');
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 13 Dec 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _WarningReasons extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_warnings_reason';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'wl_reason', 'wl_date' ), 'warnings_reason' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->groupBy = 'wl_reason';
|
||||
|
||||
foreach( \IPS\core\Warnings\Reason::roots() as $reason )
|
||||
{
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('core_warn_reason_' . $reason->id ), 'number', 'COUNT(*)', TRUE, $reason->id );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_warnings_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->extension = $this;
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'wl_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Statistics Chart Extension
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
|
||||
* @since 13 Dec 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Statistics;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics Chart Extension
|
||||
*/
|
||||
class _WarningSuspended extends \IPS\core\Statistics\Chart
|
||||
{
|
||||
/**
|
||||
* @brief Controller
|
||||
*/
|
||||
public $controller = 'core_stats_warnings_suspended';
|
||||
|
||||
/**
|
||||
* Render Chart
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL the chart is being shown on.
|
||||
* @return \IPS\Helpers\Chart
|
||||
*/
|
||||
public function getChart( \IPS\Http\Url $url ): \IPS\Helpers\Chart
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'wl_reason', 'wl_date' ), 'warnings_suspended' );
|
||||
$chart->setExtension( $this );
|
||||
|
||||
$chart->where = array( 'wl_suspend IS NOT NULL' );
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('suspended' ), 'number', 'COUNT(*)', TRUE );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_warnings_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->extension = $this;
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'wl_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
return $chart;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user