Version 4.7.23

This commit is contained in:
Neo committed 2025-12-19 16:21:27 -08:00
1 parent 7124a02564
commit 25ddeb65d6
1791 files changed
+76990 -44452

No files matched your search

@@ -102,7 +102,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
/**
* Get identifier to prevent the member being awarded points for the same action twice
* Must be unique within within of this domain, must not exceed 32 chars.
* Must be unique within of this domain, must not exceed 32 chars.
*
* @param \IPS\Member $subject The subject member
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
@@ -333,7 +333,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
if ( isset( $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) )
{
$subWhere[] = [ \IPS\Db::i()->in( 'forum_id', $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
$subWhere[] = [ \IPS\Db::i()->in( $itemClass::$databaseColumnId, $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
}
$where[] = [ 'meta_item_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
@@ -371,7 +371,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
if ( isset( $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) )
{
$subWhere[] = [ \IPS\Db::i()->in( 'forum_id', $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
$subWhere[] = [ \IPS\Db::i()->in( $itemClass::$databaseColumnId, $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
}
$where[] = [ 'feature_content_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
@@ -409,7 +409,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
if ( isset( $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) )
{
$subWhere[] = [ \IPS\Db::i()->in( 'forum_id', $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
$subWhere[] = [ \IPS\Db::i()->in( $itemClass::$databaseColumnId, $filters[ 'nodes_' . str_replace( '\\', '-', $itemClass ) ] ) ];
}
$where[] = [ 'promote_class_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
@@ -20,7 +20,14 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Achievement Action Extension
*/
class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievementAction
{
{
protected static $includeItems = FALSE;
/**
* @var bool Exclude content items that require a first comment
*/
protected static $excludeItemsWithRequiredComment = TRUE;
/**
* Get filter form elements
*
@@ -146,7 +146,7 @@ class _ConfigurationError extends \IPS\core\AdminNotification
}
/* Cache Set Up */
if ( \IPS\CACHE_METHOD AND \IPS\CACHE_METHOD != 'None' AND \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None )
if ( !\IPS\CIC AND \IPS\CACHE_METHOD AND \IPS\CACHE_METHOD != 'None' AND \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None )
{
\IPS\core\AdminNotification::send( 'core', 'ConfigurationError', 'cacheBroken', FALSE );
}
@@ -265,8 +265,7 @@ class _ConfigurationError extends \IPS\core\AdminNotification
$member->hasAcpRestriction( 'core', 'settings', 'general_manage' ) or
$member->hasAcpRestriction( 'core', 'settings', 'advanced_manage' ) or
$member->hasAcpRestriction( 'core', 'settings', 'advanced_manage' ) or
$member->hasAcpRestriction( 'core', 'overview', 'system_notifications' ) or
$member->hasAcpRestriction( 'core', 'marketplace', 'marketplace_manage' ) or
$member->hasAcpRestriction( 'core', 'overview', 'system_notifications' ) or
$member->hasAcpRestriction( 'core', 'settings', 'login_access' )
);
}
@@ -327,10 +326,6 @@ class _ConfigurationError extends \IPS\core\AdminNotification
{
return $member->hasAcpRestriction( 'core', 'overview', 'system_notifications' );
}
elseif ( $this->extra === 'marketplaceSetup' )
{
return $member->hasAcpRestriction( 'core', 'marketplace', 'marketplace_manage' );
}
elseif ( $this->extra === 'usernameLoginEnabled' )
{
return $member->hasAcpRestriction( 'core', 'settings', 'login_access' );
@@ -392,14 +387,14 @@ class _ConfigurationError extends \IPS\core\AdminNotification
{
return \IPS\Member::loggedIn()->language()->addToStack('dashboard_cic_email_quota');
}
elseif ( $this->extra === 'marketplaceSetup' )
{
return \IPS\Member::loggedIn()->language()->addToStack('marketplace_onboard_title');
}
elseif ( $this->extra === 'usernameLoginEnabled' )
{
return \IPS\Member::loggedIn()->language()->addToStack('username_login_enabled');
}
elseif( $this->extra === 'groupPromotionGroup' )
{
return \IPS\Member::loggedIn()->language()->addToStack('grouppromotion_error_group');
}
else
{
return htmlentities( $this->extra, ENT_DISALLOWED, 'UTF-8', FALSE );
@@ -461,10 +456,6 @@ class _ConfigurationError extends \IPS\core\AdminNotification
{
return \IPS\Member::loggedIn()->language()->addToStack('dashboard_cic_email_quota_subtitle');
}
elseif ( $this->extra === 'marketplaceSetup' )
{
return \IPS\Member::loggedIn()->language()->addToStack('marketplace_onboard_notification');
}
elseif ( $this->extra === 'usernameLoginEnabled' )
{
return \IPS\Member::loggedIn()->language()->addToStack('username_login_enabled_desc');
@@ -559,14 +550,14 @@ class _ConfigurationError extends \IPS\core\AdminNotification
{
return \IPS\Member::loggedIn()->language()->addToStack('dashboard_cic_email_quota_desc');
}
elseif ( $this->extra === 'marketplaceSetup' )
{
return \IPS\Theme::i()->getTemplate( 'notifications', 'core' )->marketplaceSetup();
}
elseif ( $this->extra === 'usernameLoginEnabled' )
{
return \IPS\Theme::i()->getTemplate( 'notifications', 'core' )->usernameLoginEnabled();
}
elseif( $this->extra === 'groupPromotionGroup' )
{
return \IPS\Member::loggedIn()->language()->addToStack('grouppromotion_error_group_desc');
}
}
/**
@@ -635,7 +626,7 @@ class _ConfigurationError extends \IPS\core\AdminNotification
return static::STYLE_ERROR;
}
if ( mb_substr( $this->extra, 0, 12 ) === 'supportAdmin' or $this->extra === 'origTables' or $this->extra === 'marketplaceSetup' )
if ( mb_substr( $this->extra, 0, 12 ) === 'supportAdmin' or $this->extra === 'origTables' )
{
return static::STYLE_INFORMATION;
}
@@ -660,10 +651,6 @@ class _ConfigurationError extends \IPS\core\AdminNotification
{
return \IPS\Member::load( \intval( mb_substr( $this->extra, 13 ) ) )->acpUrl();
}
elseif ( $this->extra === 'marketplaceSetup' )
{
return \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=onboard&_new=1', 'admin' );
}
else
{
return parent::link();
@@ -0,0 +1,144 @@
<?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 16 Jan 2025
*/
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 _VersionFive extends \IPS\core\AdminNotification
{
/**
* @brief Identifier for what to group this notification type with on the settings form
*/
public static $group = 'important';
/**
* @brief Priority 1-5 (1 being highest) for this group compared to others
*/
public static $groupPriority = 3;
/**
* @brief Priority 1-5 (1 being highest) for this notification type compared to others in the same group
*/
public static $itemPriority = 3;
/**
* Title for settings
*
* @return string
*/
public static function settingsTitle()
{
return 'acp_notification_VersionFive';
}
/**
* Can a member access this type of notification?
*
* @param \IPS\Member $member The member
* @return bool
*/
public static function permissionCheck( \IPS\Member $member ): bool
{
if( \IPS\CIC AND \IPS\IPS::isManaged() )
{
return FALSE;
}
return TRUE;
}
/**
* 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 TRUE;
}
/**
* Notification Title (full HTML, must be escaped where necessary)
*
* @return string
*/
public function title(): string
{
return \IPS\Member::loggedIn()->language()->addToStack('v5_notification_title');
}
/**
* Notification Body (full HTML, must be escaped where necessary)
*
* @return string
*/
public function body(): string
{
return \IPS\Member::loggedIn()->language()->addToStack('v5_notification_body' );
}
/**
* Severity
*
* @return string
*/
public function severity(): string
{
return static::SEVERITY_HIGH;
}
/**
* Dismissible?
*
* @return string
*/
public function dismissible(): string
{
return static::DISMISSIBLE_UNTIL_RECUR;
}
/**
* 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=support&controller=v5" );
}
}
@@ -77,6 +77,8 @@ class _Ckeditor
\exec( "rm -R \"{$path}/applications/core/interface/ckeditor/ckeditor/samples\"" );
}
\file_put_contents( "{$path}/applications/core/interface/ckeditor/ckeditor/legal.txt", "This software is bound by the Mozilla Public License Version 1.1\n(https://www.mozilla.org/en-US/MPL/1.1/)" );
if( \in_array( 'Release process completed:', $output ) )
{
$this->finish();
@@ -56,10 +56,13 @@ class _Codemirror
}
/* Minify and write */
require_once( \IPS\ROOT_PATH . '/system/3rd_party/JShrink/Minifier.php' );
require_once( \IPS\ROOT_PATH . '/system/3rd_party/JsMinify/Minifier.php' );
require_once( \IPS\ROOT_PATH . '/system/3rd_party/JsMinify/MinifierError.php' );
require_once( \IPS\ROOT_PATH . '/system/3rd_party/JsMinify/MinifierExpressions.php' );
\file_put_contents( \IPS\ROOT_PATH . '/applications/core/interface/codemirror/codemirror.css', $css );
$css = \IPS\Theme::minifyCss( $css );
$js = \JShrink\Minifier::minify( $js, array( 'flaggedComments' => false ) );
$js = \Garfix\JsMinify\Minifier::minify( $js, array( 'flaggedComments' => false ) );
\file_put_contents( \IPS\ROOT_PATH . '/applications/core/interface/codemirror/codemirror.js', $js );
/* Finish */
@@ -40,7 +40,7 @@ class _FacebookPixel
/**
* @brief Icon data
*/
public $icon = "facebook.png";
public $icon = "meta.png";
/**
* Constructor
@@ -40,7 +40,7 @@ class _GoogleMaps
/**
* @brief Icon data
*/
public $icon = "google_maps.jpeg";
public $icon = "google_maps.png";
/**
* Constructor
@@ -1,276 +0,0 @@
<?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' );
}
}
}
@@ -0,0 +1,170 @@
<?php
/**
* @brief Integrations: Postmark
* @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 19 November 2024
*/
namespace IPS\core\extensions\core\CommunityEnhancements;
use IPS\Email\Outgoing\Postmark;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Password;
use IPS\Helpers\Form\YesNo;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Session;
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;
}
/**
* Integrations: Postmark
*/
class _Postmark
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "postmark.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = !empty( Settings::i()->postmark_server_api_key );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$form = new Form;
$form->addHeader('postmark_settings');
$form->add( new Password( 'postmark_server_api_key', Settings::i()->postmark_server_api_key, TRUE, suffix: Member::loggedIn()->language()->addToStack( 'postmark_server_api_key_suffix' ) ) );
$form->add( new YesNo( 'postmark_track_opens', Settings::i()->postmark_track_opens, FALSE ) );
if( !empty( Settings::i()->postmark_server_api_key ) )
{
$postmark = new Postmark( Settings::i()->postmark_server_api_key );
$currentSetting = json_decode( Settings::i()->postmark_streams, TRUE );
$streams = $descriptions = [];
foreach( $postmark->api('message-streams')['MessageStreams'] as $stream )
{
if( $stream['MessageStreamType'] == 'Inbound' ) continue;
$streams[ $stream['ID'] ] = $stream['Name'];
$descriptions[ $stream['ID'] ] = '<br>'.$stream['Description'];
}
$form->addHeader('postmark_message_streams');
$form->addMessage( 'postmark_message_description');
$form->addMessage( 'postmark_stream_choose');
$form->add( new Form\Radio( 'postmark_transactional_stream', $currentSetting['transactional'], true, [ 'options' => $streams, 'descriptions' => $descriptions, 'parse' => 'normal' ] ) );
$form->add( new Form\Radio( 'postmark_bulk_stream', $currentSetting['bulk'], true, [ 'options' => $streams, 'descriptions' => $descriptions, 'parse' => 'normal' ] ) );
}
if ( $values = $form->values() )
{
try
{
$this->testSettings( $values );
}
catch ( \Exception $e )
{
Output::i()->error( $e->getMessage(), '2C438/1', 500 );
}
if( isset( $values['postmark_bulk_stream'] ) )
{
$values['postmark_streams'] = json_encode( [ 'bulk' => $values['postmark_bulk_stream'], 'transactional' => $values['postmark_transactional_stream'] ] );
unset( $values['postmark_bulk_stream'], $values['postmark_transactional_stream'] );
}
$form->saveAsSettings( $values );
Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_Postmark' => TRUE ) );
Output::i()->inlineMessage = Member::loggedIn()->language()->addToStack( 'saved' );
}
Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => Url::ips( 'docs/postmark_api_key' ),
'target' => '_blank'
),
);
Output::i()->output = $form;
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
* @throws \DomainException
*/
public function toggle( $enabled )
{
/* If we're disabling, just disable */
if ( !$enabled )
{
Settings::i()->changeValues( array( 'postmark_server_api_key' => '' ) );
}
else
{
/* We need an API key. */
throw new \DomainException;
}
}
/**
* Test Settings
*
* @param array $values Form values
* @return void
* @throws \LogicException
*/
protected function testSettings( $values )
{
/* Test Postmark settings */
$postmark = new Postmark( $values['postmark_server_api_key'] );
$server = $postmark->api('server');
if( $server === null OR !empty( $server['ErrorCode'] ) )
{
throw new \DomainException( Member::loggedIn()->language()->addToStack( 'postmark_api_error', false, [ 'sprintf' => [ (string) $server['ErrorCode'], $server['Message'] ] ] ) );
}
}
}
@@ -10,6 +10,8 @@
namespace IPS\core\extensions\core\CommunityEnhancements;
use IPS\Settings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
@@ -41,6 +43,20 @@ class _SendGrid
* @brief Icon data
*/
public $icon = "sendgrid.png";
/**
* Can we use this?
*
* @return void
*/
public static function isAvailable()
{
if( Settings::i()->sendgrid_deprecated )
{
return FALSE;
}
return TRUE;
}
/**
* Constructor
@@ -49,7 +65,7 @@ class _SendGrid
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->sendgrid_api_key && \IPS\Settings::i()->sendgrid_use_for );
$this->enabled = ( static::isAvailable() && \IPS\Settings::i()->sendgrid_api_key && \IPS\Settings::i()->sendgrid_use_for );
}
/**
@@ -66,6 +66,7 @@ class _Email
$fromName = ( \IPS\Member::loggedIn()->member_id ) ? \IPS\Member::loggedIn()->name : $values['contact_name'];
$fromEmail = ( \IPS\Member::loggedIn()->member_id ) ? \IPS\Member::loggedIn()->email : $values['email_address'];
$content = $values['contact_text'];
$referrer = $values['contact_referrer'] ?? NULL;
if ( \IPS\Settings::i()->contact_type == 'contact_internal' )
{
@@ -75,7 +76,7 @@ class _Email
{
$sender = explode( ',', \IPS\Settings::i()->contact_emails );
}
$mail = \IPS\Email::buildFromTemplate( 'core', 'contact_form', array( \IPS\Member::loggedIn(), $fromName, $fromEmail, $content ), \IPS\Email::TYPE_TRANSACTIONAL );
$mail = \IPS\Email::buildFromTemplate( 'core', 'contact_form', array( \IPS\Member::loggedIn(), $fromName, $fromEmail, $content, $referrer ), \IPS\Email::TYPE_TRANSACTIONAL );
$mail->send( $sender , array(), array(), NULL, $fromName, array( 'Reply-To' => \IPS\Email::encodeHeader( $fromName, ( \IPS\Member::loggedIn()->member_id ? \IPS\Member::loggedIn()->email : $values['email_address'] ) ) ), FALSE );
return TRUE;
@@ -22,6 +22,11 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Icons
{
/**
* @var int Cache icons for 3mo
*/
public static $cacheControlTtl = 7776000; // 3 mo
/**
* Count stored files
*
@@ -26,7 +26,7 @@ class _Theme
* Some file storage engines need to store a gzip version of some files that can be served to a browser gzipped
*/
public static $storeGzipExtensions = array( 'css', 'js' );
/**
* The configuration settings have been updated
*
@@ -175,10 +175,14 @@ class _Content
$form->add( new \IPS\Helpers\Form\YesNo( 'g_avoid_flood', $group->g_avoid_flood ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_avoid_q', $group->g_avoid_q, FALSE, array( 'togglesOff' => array( 'g_mod_preview' ), 'toggleValue' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_mod_preview', $group->g_mod_preview, FALSE, array( 'togglesOn' => array( 'g_mod_post_unit' ) ), NULL, NULL, NULL, 'g_mod_preview' ) );
$form->add( new \IPS\Helpers\Form\Custom( 'g_mod_post_unit', array( $group->g_mod_post_unit ? : 0, $group->g_bitoptions['gbw_mod_post_unit_type'] ), FALSE, array( 'getHtml' => function( $element )
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
return \IPS\Theme::i()->getTemplate( 'members' )->moderationLimits( $element->name, $element->value );
} ), NULL, NULL, NULL, 'g_mod_post_unit' ) );
$form->add( new \IPS\Helpers\Form\Custom( 'g_mod_post_unit', [$group->g_mod_post_unit ?: 0, $group->g_bitoptions[ 'gbw_mod_post_unit_type' ]], FALSE, ['getHtml' => function( $element ){
return \IPS\Theme::i()->getTemplate( 'members' )->moderationLimits( $element->name, $element->value );
}
], NULL, NULL, NULL, 'g_mod_post_unit' )
);
}
$form->add( new \IPS\Helpers\Form\YesNo( 'g_bypass_badwords', $group->g_bypass_badwords ) );
if ( \IPS\Settings::i()->ips_imagescanner_enable )
{
@@ -239,8 +243,12 @@ class _Content
}
/* Mod Queue */
$group->g_mod_post_unit = isset( $values['g_mod_post_unit'][2] ) ? 0 : $values['g_mod_post_unit'][0];
$group->g_bitoptions['gbw_mod_post_unit_type'] = isset( $values['g_mod_post_unit'][2] ) ? 0 : $values['g_mod_post_unit'][1];
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$group->g_mod_post_unit = isset( $values[ 'g_mod_post_unit' ][ 2 ] ) ? 0 : $values[ 'g_mod_post_unit' ][ 0 ];
$group->g_bitoptions['gbw_mod_post_unit_type'] = isset( $values['g_mod_post_unit'][2] ) ? 0 : $values['g_mod_post_unit'][1];
}
/* Bitwise */
$values['gbw_disable_tagging'] = \IPS\Settings::i()->tags_enabled ? !$values['gbw_disable_tagging'] : $group->g_bitoptions['gbw_disable_tagging'];
@@ -11,6 +11,9 @@
namespace IPS\core\extensions\core\IpAddresses;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Member;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -113,12 +116,13 @@ class _Content extends \IPS\Content\ExtensionGenerator
try
{
$comment = $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $comment->url(), TRUE, $comment->item()->mapped('title') );
}
catch ( \OutOfRangeException $e )
{
return '';
if( $comment->canView() )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $comment->url(), TRUE, $comment->item()->mapped('title') );
}
}
catch ( \OutOfRangeException $e ){}
return Member::loggedIn()->language()->addToStack( 'ipaddress_no_permission' );
}
);
@@ -154,7 +158,16 @@ class _Content extends \IPS\Content\ExtensionGenerator
}
}
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] )->url(), TRUE, $val );
try
{
$item = $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] );
if( $item->canView() )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $item->url(), TRUE, $val );
}
}
catch( \OutOfRangeException ){}
return Member::loggedIn()->language()->addToStack( 'ipaddress_no_permission' );
},
);
if( isset( $class::$databaseColumnMap['container'] ) )
@@ -163,7 +176,11 @@ class _Content extends \IPS\Content\ExtensionGenerator
{
$nodeClass = $class::$containerNodeClass;
$node = $nodeClass::load( $val );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $node->url(), TRUE, $node->_title );
if( $node->canView() )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $node->url(), TRUE, $node->_title );
}
return Member::loggedIn()->language()->addToStack( 'ipaddress_no_permission' );
};
}
@@ -22,17 +22,6 @@ 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?
*
@@ -30,7 +30,7 @@ class _Logins
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_members_known_ip_addresses', [ 'ip_address=?', '' ] , [ "ip_address != '' AND last_seen <?", $time ] );
// Main cleanup task takes care of this
}
/**
@@ -30,7 +30,7 @@ class _MemberHistory
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_member_history', [ 'log_ip_address=?', '' ] , [ "log_ip_address != '' AND log_date <?", $time ] );
\IPS\Db::i()->update('core_member_history', [ 'log_ip_address' => '' ] , [ "log_ip_address != '' and log_date <?", $time ] );
}
/**
@@ -30,7 +30,7 @@ class _Ratings
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_ratings', [ 'ip=?', '' ] , [ "ip != '' AND rating_date <?", $time ] );
\IPS\Db::i()->update('core_ratings', [ 'ip' => '' ] , [ "ip != '' AND rating_date IS NOT NULL AND rating_date <?", $time ] );
}
/**
@@ -30,7 +30,7 @@ class _Registration
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_members', [ 'ip_address=?', '' ] , [ "ip_address != '' AND joined <?", $time ] );
\IPS\Db::i()->update('core_members', [ 'ip_address' => '' ] , [ "ip_address != '' and joined <?", $time ] );
}
/**
@@ -30,7 +30,7 @@ class _ReportComments
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_rc_comments', [ 'ip_address=?', '' ] , [ 'comment_date <?', $time ] );
\IPS\Db::i()->update('core_rc_comments', [ 'ip_address' => '' ] , [ 'comment_date <?', $time ] );
}
/**
@@ -30,7 +30,7 @@ class _Reports
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_rc_reports', [ 'ip_address=?', '' ] , [ 'date_reported <?', $time ] );
\IPS\Db::i()->update('core_rc_reports', [ 'ip_address' => '' ] , [ 'date_reported <?', $time ] );
}
/**
@@ -30,7 +30,7 @@ class _Votes
*/
public function pruneIpAddresses(int $time)
{
\IPS\Db::i()->update('core_voters', [ 'ip_address=?', '' ] , [ "ip_address != '' AND vote_date <?", $time ] );
\IPS\Db::i()->update('core_voters', [ 'ip_address' => '' ] , [ "ip_address != '' and vote_date <?", $time ] );
}
/**
@@ -29,7 +29,7 @@ class _DevicesAndIPAddresses extends \IPS\core\MemberACPProfile\Block
*/
public function output()
{
$lastUsedIp = $this->member->lastUsedIp();
$lastUsedIp = $this->member->lastUsedIp() ?? '';
$devices = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members_known_devices', array( 'member_id=?', $this->member->member_id ), 'last_seen DESC', 5 ), 'IPS\Member\Device' );
return \IPS\Theme::i()->getTemplate('memberprofile')->devicesAndIPAddresses( $this->member, $lastUsedIp, $devices );
@@ -194,10 +194,24 @@ class _ProfileFields
$type = 'IPS\Helpers\Form\\' . $field['pf_type'];
$helper = NULL;
$langKey = "core_pfield_{$id}";
if( empty( $data[ $langKey ] ) )
/* We need to handle YesNo and Checkbox fields specially, as it will intentionally try and pass '0' which is valid, but returns true in empty() */
switch( $type )
{
continue;
case 'IPS\Helpers\Form\YesNo':
case 'IPS\Helpers\Form\Checkbox':
if ( !isset( $data[ $langKey ] ) OR $data[ $langKey ] === '' )
{
continue 2;
}
break;
default:
if ( empty( $data[ $langKey ] ) )
{
continue 2;
}
break;
}
switch ( $type )
@@ -1,114 +0,0 @@
<?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 ) { }
}
}
@@ -166,7 +166,6 @@ class _System
\IPS\Db::i()->delete( 'core_follow_count_cache', array( 'id=? AND class=?', $member2->member_id, 'IPS\core\Member' ) );
\IPS\Db::i()->delete( 'core_acp_notifcations_dismissals', array( '`member`=?', $member2->member_id ) );
\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] );
@@ -327,7 +326,6 @@ class _System
array( 'core_referrals', 'referred_by' ),
array( 'core_referrals', 'member_id' ),
array( 'core_anonymous_posts', 'anonymous_member_id' ),
array( 'core_marketplace_tokens', 'id' ),
array( 'core_member_badges', 'member' ),
array( 'core_notifications_pwa_keys', 'member' ),
array( 'core_achievements_log_milestones', 'milestone_member_id' ),
@@ -142,7 +142,7 @@ class _Content
/* Display */
$pagination = trim( \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( \IPS\Http\Url::internal( "app=core&module=modcp&controller=modcp&tab=hidden", 'front', 'modcp_content' ), ceil( $results->count( TRUE ) / $query->resultsToGet ), $page, $query->resultsToGet ) );
return \IPS\Theme::i()->getTemplate( 'modcp' )->tableWrapper( \IPS\Theme::i()->getTemplate('search')->resultStream( $results, $pagination, \IPS\Http\Url::internal( "app=core&module=modcp&controller=modcp&tab=hidden", 'front', 'modcp_content' ), TRUE ), 'modcp_hidden' );
return \IPS\Theme::i()->getTemplate( 'modcp' )->tableWrapper( \IPS\Theme::i()->getTemplate('search')->resultStream( $results, $pagination, \IPS\Http\Url::internal( "app=core&module=modcp&controller=modcp&tab=hidden", 'front', 'modcp_content' ), TRUE, $results->count( TRUE ) ), 'modcp_hidden' );
}
}
}
@@ -11,6 +11,19 @@
namespace IPS\core\extensions\core\ModCp;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\core\Reports\Report;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Select;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use function count;
use function intval;
use function iterator_to_array;
use function rtrim;
use function str_replace;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -35,7 +48,7 @@ class _Reports extends \IPS\Content\Controller
public function getTab()
{
/* Check Permissions */
if ( ! \IPS\Member::loggedIn()->modPermission('can_view_reports') )
if ( ! Member::loggedIn()->modPermission('can_view_reports') )
{
return null;
}
@@ -50,18 +63,18 @@ class _Reports extends \IPS\Content\Controller
*/
public function execute()
{
if ( !\IPS\Member::loggedIn()->modPermission( 'can_view_reports' ) )
if ( !Member::loggedIn()->modPermission( 'can_view_reports' ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C139/1', 403, '' );
Output::i()->error( 'no_module_permission', '2C139/1', 403, '' );
}
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp.css' ) );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp_responsive.css' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp_responsive.css' ) );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_modcp.js', 'core' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front_modcp.js', 'core' ) );
parent::execute();
}
@@ -77,7 +90,7 @@ class _Reports extends \IPS\Content\Controller
$where = [ \IPS\core\Reports\Report::where() ];
/* Make sure we're loading the correct statuses */
if( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->overview ) )
if( Request::i()->isAjax() and isset( Request::i()->overview ) )
{
$where[] = array( "status IN( 1,2 )" );
}
@@ -117,22 +130,23 @@ class _Reports extends \IPS\Content\Controller
}
$table->filters['filter_report_status_1_' . $appKey ] = [ 'status=1 and ' . \IPS\Db::i()->in( 'class', $classes ) ];
\IPS\Member::loggedIn()->language()->words[ 'filter_report_status_1_' . $appKey ] = \IPS\Member::loggedIn()->language()->addToStack( 'filter_report_status_1_app', NULL, [ 'sprintf' => [ \IPS\Application::applications()[ $appKey ]->_title ] ] );
Member::loggedIn()->language()->words[ 'filter_report_status_1_' . $appKey ] = Member::loggedIn()->language()->addToStack( 'filter_report_status_1_app', NULL, [ 'sprintf' => [ \IPS\Application::applications()[ $appKey ]->_title ] ] );
}
$table->filters['filter_report_status_2'] = [ 'status=2' ];
$table->filters['filter_report_status_3'] = [ 'status=3' ];
$table->title = \IPS\Member::loggedIn()->language()->addToStack( 'report_center_header' );
$table->filters['filter_report_status_4'] = [ 'status=4' ];
$table->title = Member::loggedIn()->language()->addToStack( 'report_center_header' );
if ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->overview ) )
if ( Request::i()->isAjax() and isset( Request::i()->overview ) )
{
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'modcp', 'core' ), 'reportListOverview' );
\IPS\Output::i()->json( array( 'data' => (string) $table ) );
Output::i()->json( array( 'data' => (string) $table ) );
}
else
{
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports' );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack( 'modcp_reports' ) );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'modcp_reports' );
return \IPS\Theme::i()->getTemplate( 'modcp' )->reportList( (string) $table );
}
}
@@ -147,16 +161,16 @@ class _Reports extends \IPS\Content\Controller
/* Load Report */
try
{
$report = \IPS\core\Reports\Report::loadAndCheckPerms( \IPS\Request::i()->id );
$report = \IPS\core\Reports\Report::loadAndCheckPerms( Request::i()->id );
$report->markRead();
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C139/3', 404, '' );
Output::i()->error( 'node_error', '2C139/3', 404, '' );
}
/* Check permission. We do it this way rather than loadAndCheckPerms() since we need to know if the user *had* permission if the content has been deleted */
$allowedPermIds = iterator_to_array( \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', \IPS\Member::loggedIn()->permissionArray() ) . " OR perm_view='*'" ) );
$allowedPermIds = iterator_to_array( \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', Member::loggedIn()->permissionArray() ) . " OR perm_view='*'" ) );
$workingClass = $report->class;
if ( isset( $workingClass::$itemClass ) )
{
@@ -168,43 +182,31 @@ class _Reports extends \IPS\Content\Controller
}
if ( isset( $workingClass::$permissionMap ) and isset( $workingClass::$permissionMap['read'] ) and $workingClass::$permissionMap['read'] !== 'view' )
{
$allowedPermIds = array_intersect( $allowedPermIds, iterator_to_array( \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_' . $workingClass::$permissionMap['read'], \IPS\Member::loggedIn()->permissionArray() ) . " OR perm_" . $workingClass::$permissionMap['read'] . "='*'" ) ) );
$allowedPermIds = array_intersect( $allowedPermIds, iterator_to_array( \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_' . $workingClass::$permissionMap['read'], Member::loggedIn()->permissionArray() ) . " OR perm_" . $workingClass::$permissionMap['read'] . "='*'" ) ) );
}
if ( \in_array( 'IPS\Content\Permissions', class_implements( $workingClass ) ) and !\in_array( $report->perm_id, $allowedPermIds ) )
{
\IPS\Output::i()->error( 'node_error_no_perm', '2C139/5', 403, '' );
Output::i()->error( 'node_error_no_perm', '2C139/5', 403, '' );
}
/* Setting status? */
if( isset( \IPS\Request::i()->setStatus ) and \in_array( \IPS\Request::i()->setStatus, range( 1, 3 ) ) )
if( isset( Request::i()->setStatus ) and \in_array( Request::i()->setStatus, range( 1, 4 ) ) )
{
\IPS\Session::i()->csrfCheck();
$report->changeStatus( (int) Request::i()->setStatus );
$report->status = (int) \IPS\Request::i()->setStatus;
$report->save();
/* Post a comment on the report */
$content = \IPS\Member::loggedIn()->language()->addToStack( 'update_report_status_content', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'report_status_' . $report->status ) ) ) );
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );
$comment = \IPS\core\Reports\Comment::create( $report, $content, TRUE, NULL, NULL, \IPS\Member::loggedIn(), new \IPS\DateTime );
$comment->save();
/* And add to the moderator log */
\IPS\Session::i()->modLog( 'modlog__action_update_report_status', array( $report->url()->__toString() => FALSE ) );
\IPS\Output::i()->redirect( $report->url() );
Output::i()->redirect( $report->url()->setQueryString( 'changed', '1' ) );
}
/* Deleting? */
if( isset( \IPS\Request::i()->_action ) and \IPS\Request::i()->_action == 'delete' and $report->canDelete() )
if( isset( Request::i()->_action ) and Request::i()->_action == 'delete' and $report->canDelete() )
{
\IPS\Session::i()->csrfCheck();
$report->delete();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports', NULL, 'modcp_reports' ) );
Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports', NULL, 'modcp_reports' ) );
}
/* Load */
@@ -252,7 +254,7 @@ class _Reports extends \IPS\Content\Controller
catch ( \OutOfRangeException $e ) { }
/* Next/Previous Links */
$permSubQuery = \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', array_merge( array( \IPS\Member::loggedIn()->member_group_id ) , array_filter( explode( ',', \IPS\Member::loggedIn()->mgroup_others ) ) ) ) . " or perm_view='*'" );
$permSubQuery = \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', array_merge( array( Member::loggedIn()->member_group_id ) , array_filter( explode( ',', Member::loggedIn()->mgroup_others ) ) ) ) . " or perm_view='*'" );
$prevReport = NULL;
$prevItem = NULL;
@@ -298,20 +300,160 @@ class _Reports extends \IPS\Content\Controller
catch ( \UnderflowException $e ) {}
/* Display */
if ( \IPS\Request::i()->isAjax() and !isset( \IPS\Request::i()->_contentReply ) and !isset( \IPS\Request::i()->getUploader ) AND !isset( \IPS\Request::i()->page ) AND !isset( \IPS\Request::i()->_previewField ) )
if ( Request::i()->isAjax() and !isset( Request::i()->_contentReply ) and !isset( Request::i()->getUploader ) AND !isset( Request::i()->page ) AND !isset( Request::i()->_previewField ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'modcp' )->reportPanel( $report, $comment, $ref );
Output::i()->output = \IPS\Theme::i()->getTemplate( 'modcp' )->reportPanel( $report, $comment, $ref );
}
else
{
$sprintf = $item ? htmlspecialchars( $item->mapped('title'), ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : \IPS\Member::loggedIn()->language()->addToStack('content_deleted');
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports_view', FALSE, array( 'sprintf' => array( $sprintf ) ) );
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports', 'front', 'modcp_reports' ), \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports' ) );
\IPS\Output::i()->breadcrumb[] = array( NULL, $item ? $item->mapped('title') : \IPS\Member::loggedIn()->language()->addToStack( 'content_deleted' ) );
if ( isset( Request::i()->changed ) )
{
/* Show a flash message */
Output::i()->inlineMessage = Member::loggedIn()->language()->addToStack( 'report_status_changed');
}
$sprintf = $item ? htmlspecialchars( $item->mapped('title'), ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : Member::loggedIn()->language()->addToStack('content_deleted');
Output::i()->title = Member::loggedIn()->language()->addToStack( 'modcp_reports_view', FALSE, array( 'sprintf' => array( $sprintf ) ) );
Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports', 'front', 'modcp_reports' ), Member::loggedIn()->language()->addToStack( 'modcp_reports' ) );
Output::i()->breadcrumb[] = array( NULL, $item ? $item->mapped('title') : Member::loggedIn()->language()->addToStack( 'content_deleted' ) );
return \IPS\Theme::i()->getTemplate( 'modcp' )->report( $report, $comment, $item, $ref, $prevReport, $prevItem, $nextReport, $nextItem, $delLog );
}
}
/**
* Show the report center modal
*
* @return void
*/
public function reportCenterConfirmModal()
{
$id = intval( Request::i()->id );
try
{
$report = Report::load( $id );
}
catch( \OutOfRangeException $e )
{
Output::i()->error( 'node_error', '2S319/1', 404 );
}
$form = new Form( 'form', 'modal_author_notification_form_submit' );
$form->class .= 'ipsForm_vertical';
$form->addButton( 'cancel', 'link', $report->url() );
/* Get the optional notifications */
$notifications = [ 0 => 'modal_author_notification_none' ];
foreach( Report::getAuthorNotifications() as $notification )
{
$notifications[ $notification['id'] ] = $notification['title'];
}
$form->add( new Select( 'modal_author_notification', null, null, [ 'options' => $notifications ] ) );
try
{
$class = $report->class;
$thing = $class::load( $report->content_id );
}
catch( \OutOfRangeException $e )
{
Output::i()->error( 'node_error', '2S319/2', 404 );
}
/* Fix the language string */
Member::loggedIn()->language()->words['modal_author_notification'] = Member::loggedIn()->language()->addToStack('modal_author__notification', null, [ 'sprintf' => [ $thing->author()->name ] ] );
if ( $values = $form->values() )
{
\IPS\Session::i()->csrfCheck();
$report->changeStatus( (int) Request::i()->status, $values['modal_author_notification'] );
Output::i()->redirect( $report->url()->setQueryString( 'changed', '1' ) );
}
Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->genericBlock( (string) $form, null, 'ipsPadding' ) );
}
/**
* Change Report Reason (report_type)
*
* @return void
*/
public function changeType()
{
/* Load the report and the main object */
try
{
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', [ 'id=?', Request::i()->id ] )->first();
$item = \IPS\core\Reports\Report::loadAndCheckPerms( $report['rid'] );
}
catch ( \Exception $e )
{
Output::i()->error( 'node_error', '2C139/6', 404, '' );
}
/* Build form */
$form = new Form;
$options = array( \IPS\core\Reports\Report::TYPE_MESSAGE => Member::loggedIn()->language()->addToStack('report_message_comment') );
foreach( \IPS\core\Reports\Types::roots() as $type )
{
$options[ $type->id ] = $type->_title;
}
if ( count( $options ) > 1 )
{
$form->add( new Select( 'report_type', $report['report_type'], null, [ 'options' => $options ] ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'report_type_change_log', true, null ) );
}
$form->class .= 'ipsForm_vertical';
/* Handle submissions */
if ( $values = $form->values() )
{
/* Update the new reason */
\IPS\Db::i()->update( 'core_rc_reports', array( 'report_type' => $values['report_type'] ), [ 'id=?', $report['id'] ] );
if ( ! empty( $values['report_type_change_log'] ) )
{
$old = Member::loggedIn()->language()->get('none');
$new = Member::loggedIn()->language()->get('none');
if ( $values['report_type'] )
{
$new = \IPS\core\Reports\Types::load( $values['report_type'] )->_title;
}
if ( $report['report_type'] )
{
try
{
$old = \IPS\core\Reports\Types::load( $report['report_type'] )->_title;
}
catch ( \OutOfRangeException $e )
{
Output::i()->error( 'node_error', '2C139/7', 403, '' );
}
}
if ( $report['report_type'] != $values['report_type'] )
{
$url = $item->url()->setQueryString( 'tab', 'reports' )->setFragment( 'report' . $report['id'] );
$content = Member::loggedIn()->language()->addToStack('report_type_changed', FALSE, array( 'sprintf' => array( $new, $old, $url, $report['id'] ) ) );
Member::loggedIn()->language()->parseOutputForDisplay( $content );
$content = str_replace( rtrim( \IPS\Settings::i()->base_url, '/' ), '<___base_url___>', $content );
$comment = \IPS\core\Reports\Comment::create( $item, $content, TRUE, NULL, NULL, Member::loggedIn(), new \IPS\DateTime );
$comment->save();
}
}
Output::i()->redirect( $item->url() );
}
/* Display form */
Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
/**
* Redirect to the original content for a report
*
@@ -321,19 +463,19 @@ class _Reports extends \IPS\Content\Controller
{
try
{
$report = \IPS\core\Reports\Report::loadAndCheckPerms( \IPS\Request::i()->id );
$report = \IPS\core\Reports\Report::loadAndCheckPerms( Request::i()->id );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C139/4', 404, '' );
Output::i()->error( 'node_error', '2C139/4', 404, '' );
}
$reportClass = $report->class;
$comment = $reportClass::load( $report->content_id );
$url = \IPS\Request::i()->parent ? $comment->item()->url() : $comment->url();
$url = Request::i()->parent ? $comment->item()->url() : $comment->url();
$url = $url->setQueryString( '_report', $report->id );
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
/**
@@ -345,12 +487,12 @@ class _Reports extends \IPS\Content\Controller
{
try
{
$report = \IPS\core\Reports\Report::loadAndCheckPerms( \IPS\Request::i()->id );
$comment = \IPS\core\Reports\Comment::load( \IPS\Request::i()->comment );
$report = \IPS\core\Reports\Report::loadAndCheckPerms( Request::i()->id );
$comment = \IPS\core\Reports\Comment::load( Request::i()->comment );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C139/4', 404, '' );
Output::i()->error( 'node_error', '2C139/4', 404, '' );
}
$url = $report->url()->setQueryString( 'activeTab', 'comments' );
@@ -366,6 +508,6 @@ class _Reports extends \IPS\Content\Controller
}
\IPS\Output::i()->redirect( $url->setFragment( 'comment-' . $comment->$commentIdColumn ) );
Output::i()->redirect( $url->setFragment( 'comment-' . $comment->$commentIdColumn ) );
}
}
@@ -439,13 +439,27 @@ class _Content
{
$where[] = array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['hidden'] . ' NOT IN(?,?)', -2, -3 );
}
/* Skip Anonymous authors, we'll do this later */
if( isset( $commentClass::$databaseColumnMap['is_anon'] ) )
{
$where[] = array( "( " . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['is_anon'] . ' IS NULL OR ' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['is_anon'] . '!=? )', 1);
}
$commenters = \IPS\Db::i()->select( 'DISTINCT ' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['author'], $commentClass::$databaseTable, $where );
$names = array();
$comment = $commentClass::loadAndCheckPerms( $notification->item_sub_id );
/* If we have an anonymous author, add "anonymous" to the list */
if( $comment->isAnonymous() )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'post_anonymously_placename' );
}
foreach ( $commenters as $member )
{
$name = ( $item->isAnonymous() ) ? \IPS\Member::loggedIn()->language()->addToStack( 'post_anonymously_placename' ) : \IPS\Member::load( $member )->name;
$name = \IPS\Member::load( $member )->name;
if ( \count( $names ) > 2 )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'x_others', FALSE, array( 'pluralize' => array( \count( $commenters ) - 3 ) ) );
@@ -453,8 +467,6 @@ class _Content
}
$names[] = $name;
}
$comment = $commentClass::loadAndCheckPerms( $notification->item_sub_id );
/* If the comment is in the deletion queue, add the showDeleted=1 parameter otherwise it just won't show up */
$url = $comment->url('find');
@@ -463,9 +475,9 @@ class _Content
{
$url = $url->setQueryString( 'showDeleted', 1 );
}
/* Unread? */
$unread = false;
$unread = true;
if ( $item->timeLastRead() instanceof \IPS\DateTime )
{
$unread = (bool) ( $item->timeLastRead()->getTimestamp() < $notification->updated_time->getTimestamp() );
@@ -11,6 +11,9 @@
namespace IPS\core\extensions\core\Notifications;
/* To prevent PHP errors (extending class does not exist) revealing path */
use function md5;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -115,7 +118,21 @@ class _Moderation
'disabled' => array(),
);
}
/* Do we have any report types, and can we report things? ? */
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_automatic_moderation_types' )->first() and ( ! $member or $member->group['g_can_report'] ) )
{
$return['reports'] = [
'type' => 'standard',
'notificationTypes' => ['report_outcome'],
'title' => 'notifications__core_Moderation_reports',
'showTitle' => true,
'description' => 'notifications__core_Moderation_reports_desc',
'default' => ['email'],
'disabled' => [ 'push', 'inline' ],
];
}
return $return;
}
@@ -190,6 +207,52 @@ class _Moderation
return $return;
}
/**
* Parse notification: Report outcomes
*
* @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_report_outcome( $notification, $htmlEscape=TRUE )
{
if ( ! $notification->item_sub or ! count( $notification->extra ) )
{
throw new \OutOfRangeException;
}
$reported = $notification->item_sub;
$item = ( $reported instanceof \IPS\Content\Item ) ? $reported : $reported->item();
try
{
$reportType = \IPS\core\Reports\Types::load( $notification->extra['reportType'] );
$class = $item->class;
$url = $class::load( $item->content_id )->url();
}
catch( \UnderflowException )
{
throw new \OutOfRangeException;
}
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__report_outcome', FALSE, array(
( $htmlEscape ? 'sprintf' : 'htmlsprintf' ) => array( $item->mapped('title'), $reportType->_title, \IPS\Member::loggedIn()->language()->addToStack( 'report_status_' . $item->status ) ) )
),
'url' => $url,
'author' => \IPS\Member::loggedIn(),
);
}
/**
* Parse notification: unapproved_content_bulk
@@ -497,10 +560,18 @@ class _Moderation
if ( $item instanceof \IPS\Content\Comment OR $item instanceof \IPS\Content\Review )
{
$title = $item->item()->mapped('title');
/* Unread? */
$unread = false;
if ( $item->item()->timeLastRead() instanceof \IPS\DateTime )
{
$unread = (bool) ( $item->item()->timeLastRead()->getTimestamp() < $notification->updated_time->getTimestamp() );
}
}
else
{
$title = $item->mapped('title');
$unread = (bool) ( $item->unread() );
}
$name = ( $item->isAnonymous() ) ? \IPS\Member::loggedIn()->language()->addToStack( 'post_anonymously_placename' ) : $item->author()->name;
@@ -510,6 +581,7 @@ class _Moderation
'url' => $item->url(),
'content' => $item->content(),
'author' => $item->author(),
'unread' => $unread,
);
}
@@ -104,7 +104,7 @@ class _Devices
}
}
}
$where[] = ['type=?', 'devices'];
$result = \IPS\Db::i()->select( 'SUM(value_1) as mobiles, SUM(value_2) AS tablets, SUM(value_3) as consoles, SUM(value_4) as desktops', 'core_statistics', $where )->first();
$total = $result['mobiles'] + $result['tablets'] + $result['consoles'] + $result['desktops'];
@@ -145,7 +145,7 @@ class _ProfileFields
$pieBarData[] = array(
'name' => \IPS\Member::loggedIn()->language()->addToStack('stats_pfields__checked'),
'value' => isset( $results[1] ) ? $results[1]['total'] : 0,
'percentage' => ( isset( $results[1] ) AND $results[0]['total'] > 0 ) ? round( ( $results[1]['total'] / $total ) * 100, 2 ) : 0
'percentage' => ( isset( $results[1] ) AND $results[1]['total'] > 0 ) ? round( ( $results[1]['total'] / $total ) * 100, 2 ) : 0
);
}
elseif( $field['pf_type'] == 'YesNo' )
@@ -47,6 +47,8 @@ class _MemberContent
{
return NULL;
}
$data['last_id'] = 0;
/* Get count - only do this if we're deleting member content, not guest content */
if ( $data['member_id'] )
@@ -77,21 +79,25 @@ class _MemberContent
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
public function run( &$data, $offset )
{
$classname = $data['class'];
$idColumn = $classname::$databaseColumnId;
$exploded = explode( '\\', $classname );
if ( !class_exists( $classname ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
$select = $classname::db()->select( '*', $classname::$databaseTable, static::_getWhere( $data ), $classname::$databasePrefix.$classname::$databaseColumnId, array( 0, \IPS\REBUILD_NORMAL ) );
$select = $classname::db()->select( '*', $classname::$databaseTable, static::_getWhere( $data ), $classname::$databasePrefix.$classname::$databaseColumnId, \IPS\REBUILD_SLOW );
/* Keep track of how many we did since we cannot rely on the count for guests */
$did = 0;
foreach ( new \IPS\Patterns\ActiveRecordIterator( $select, $classname ) as $item )
{
$did++;
$data['last_id'] = $item->$idColumn;
/* If this is the first comment on an item where a first comment is required (e.g. posts) do nothing, as when we get to the item, that will handle it */
if ( $item instanceof \IPS\Content\Comment )
{
@@ -108,7 +114,7 @@ class _MemberContent
}
catch( \OutOfRangeException $e ) {}
}
continue;
}
}
@@ -142,29 +148,16 @@ class _MemberContent
}
break;
}
$did++;
}
catch( \OutOfRangeException $e )
{
$did++;
}
catch( \ErrorException $e )
{
$did++;
}
catch( \OutOfRangeException | \ErrorException $e ) {}
}
if ( !$did )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
else if( isset( $data['count'] ) AND ( $offset + \IPS\REBUILD_NORMAL >= $data['count'] ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
return ( $offset + \IPS\REBUILD_NORMAL );
return ( $offset + $did );
}
/**
@@ -216,11 +209,19 @@ class _MemberContent
protected static function _getWhere( $data )
{
$classname = $data['class'];
$where = array( array( $classname::$databasePrefix . $classname::$databaseColumnMap['author'] . '=?', $data['member_id'] ) );
$where = [
[
$classname::$databasePrefix . $classname::$databaseColumnId . '>?', $data['last_id']
]
];
if ( !$data['member_id'] )
{
$where = array( $classname::$databasePrefix . $classname::$databaseColumnMap['author_name'] . '=?', $data['name'] );
$where[] = [ $classname::$databasePrefix . $classname::$databaseColumnMap['author_name'] . '=?', $data['name'] ];
}
else
{
$where[] = [ $classname::$databasePrefix . $classname::$databaseColumnMap['author'] . '=?', $data['member_id'] ];
}
if ( $data['action'] == 'hide' )
@@ -217,6 +217,7 @@ class _RebuildAchievements
public function postComplete( $data, $processed = TRUE )
{
/* The core_queue row is deleted before this is run */
/* @note SELECT_FROM_WRITE_SERVER: Avoid race conditions with the task */
if ( ! \IPS\Db::i()->select( 'COUNT(*)', 'core_queue', [ [ '`key`=?', 'RebuildAchievements' ] ], NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() )
{
/* Rebuilding is complete! */
@@ -61,9 +61,52 @@ class _RebuildSearchIndex
if( \IPS\Settings::i()->search_method == 'mysql' and \IPS\Settings::i()->search_index_timeframe )
{
if( isset( $classname::$databaseColumnMap['date'] ) )
$cutoff = \IPS\DateTime::ts( time() - ( 86400 * \IPS\Settings::i()->search_index_timeframe ) )->getTimestamp();
/* If we store the time of the last comment / review, use that. If not, then use normal start date. */
if ( isset( $classname::$databaseColumnMap['last_comment'] ) OR isset( $classname::$databaseColumnMap['last_review'] ) )
{
$where[] = array( $classname::$databasePrefix . $classname::$databaseColumnMap['date'] . '> ?', \IPS\DateTime::ts( time() - ( 86400 * \IPS\Settings::i()->search_index_timeframe ) )->getTimestamp() );
$columns = array();
$binds = array();
if ( isset( $classname::$databaseColumnMap['last_comment'] ) )
{
if ( is_array( $classname::$databaseColumnMap['last_comment'] ) )
{
foreach( $classname::$databaseColumnMap['last_comment'] AS $column )
{
$columns[] = $classname::$databasePrefix . $column . " > ?";
$binds[] = $cutoff;
}
}
else
{
$columns[] = $classname::$databasePrefix . $classname::$databaseColumnMap['last_comment'] . " > ?";
$binds[] = $cutoff;
}
}
if ( isset( $classname::$databaseColumnMap['last_review'] ) )
{
if ( is_array( $classname::$databaseColumnMap['last_review'] ) )
{
foreach( $classname::$databaseColumnMap['last_review'] AS $column )
{
$columns[] = $classname::$databasePrefix . $column . " > ?";
$binds[] = $cutoff;
}
}
else
{
$columns[] = $classname::$databasePrefix . $classname::$databaseColumnMap['last_review'] . " > ?";
$binds[] = $cutoff;
}
}
$where[] = array( '(' . implode( ' OR ', $columns ) . ')', ...$binds );
}
else if( isset( $classname::$databaseColumnMap['date'] ) )
{
$where[] = array( $classname::$databasePrefix . $classname::$databaseColumnMap['date'] . '> ?', $cutoff );
}
}
@@ -11,6 +11,16 @@
namespace IPS\core\extensions\core\Sitemap;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Db;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use function array_merge;
use function class_implements;
use function implode;
use function json_encode;
use function md5;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -182,14 +192,54 @@ class _Content extends \IPS\Content\ExtensionGenerator
if ( isset( $class::$containerNodeClass ) )
{
$nodeClass = $class::$containerNodeClass;
$containerClass = $class::$containerNodeClass;
/* We need one file for the nodes */
$files[] = 'sitemap_content_' . str_replace( '\\', '_', mb_substr( $nodeClass, 4 ) );
}
$files[] = 'sitemap_content_' . str_replace( '\\', '_', mb_substr( $containerClass, 4 ) );
$where = [];
/* And however many for the content items */
$contentItems = $class::getItemsWithPermission( $class::sitemapWhere(), NULL, NULL, 'read', \IPS\Content\Hideable::FILTER_PUBLIC_ONLY, 0, new \IPS\Member, NULL, NULL, NULL, TRUE );
/* Get the count in the most efficient way possible */
if ( \in_array( 'IPS\Content\Permissions', class_implements( $class ) ) )
{
$member = new Member;
$containerWhere = [];
$categories = [];
/* @var $permissionMap array */
$permQuery = \IPS\Db::i()->select( 'perm_type_id', 'core_permission_index', array( "core_permission_index.app='" . $containerClass::$permApp . "' AND core_permission_index.perm_type='" . $containerClass::$permType . "' AND (" . \IPS\Db::i()->findInSet( 'perm_' . $containerClass::$permissionMap['read'], $member->permissionArray() ) . ' OR ' . 'perm_' . $containerClass::$permissionMap['read'] . "='*' )" ) );
/* If we cannot access clubs, skip them */
if ( \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) AND !$member->canAccessModule( \IPS\Application\Module::get( 'core', 'clubs', 'front' ) ) )
{
$containerWhere[] = array( $containerClass::$databaseTable . '.' . $containerClass::$databasePrefix . $containerClass::clubIdColumn() . ' IS NULL' );
}
if ( count( $containerWhere ) )
{
$permQuery->join( $containerClass::$databaseTable, array_merge( $containerWhere, array( 'core_permission_index.perm_type_id=' . $containerClass::$databaseTable . '.' . $containerClass::$databasePrefix . $containerClass::$databaseColumnId ) ), 'STRAIGHT_JOIN' );
}
foreach( $permQuery as $result )
{
$categories[] = $result;
}
if( count( $categories ) )
{
$where[] = array( $containerClass::$databasePrefix . $containerClass::$databaseColumnId . ' IN(' . implode( ',', $categories ) . ')' );
}
}
$contentItems = 0;
foreach( new ActiveRecordIterator( Db::i()->select( '*', $containerClass::$databaseTable, $where ), $containerClass ) as $node )
{
$contentItems += $node->_items;
}
}
else
{
/* And however many for the content items */
$contentItems = $class::getItemsWithPermission( $class::sitemapWhere(), NULL, NULL, 'read', \IPS\Content\Hideable::FILTER_PUBLIC_ONLY, 0, new \IPS\Member, NULL, NULL, NULL, TRUE );
}
/* Choose which count to use to calculate the number of filess */
$usedCount = ( $requestedCount > $contentItems OR $requestedCount <= 0 ) ? $contentItems : $requestedCount;
@@ -226,7 +276,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
$select = array();
if ( \in_array( 'IPS\Content\Permissions', class_implements( $nodeClass ) ) or \in_array( 'IPS\Node\Permissions', class_implements( $nodeClass ) ) )
{
$select = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', $nodeClass::$databaseTable, array( '(' . \IPS\Db::i()->findInSet( "perm_{$nodeClass::$permissionMap['read']}", array( \IPS\Settings::i()->guest_group ) ) . ' OR ' . "perm_{$nodeClass::$permissionMap['read']}=? )", '*' ) )->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id={$nodeClass::$databaseTable}.{$nodeClass::$databasePrefix}{$nodeClass::$databaseColumnId}", $nodeClass::$permApp, $nodeClass::$permType ) ), $nodeClass );
$select = new ActiveRecordIterator( \IPS\Db::i()->select( '*', $nodeClass::$databaseTable, array( '(' . \IPS\Db::i()->findInSet( "perm_{$nodeClass::$permissionMap['read']}", array( \IPS\Settings::i()->guest_group ) ) . ' OR ' . "perm_{$nodeClass::$permissionMap['read']}=? )", '*' ) )->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id={$nodeClass::$databaseTable}.{$nodeClass::$databasePrefix}{$nodeClass::$databaseColumnId}", $nodeClass::$permApp, $nodeClass::$permType ) ), $nodeClass );
}
else if ( $nodeClass::$ownerTypes !== NULL and is_subclass_of( $nodeClass, 'IPS\Node\Model' ) )
{
@@ -25,7 +25,7 @@ class _Keywords extends \IPS\core\Statistics\Chart
/**
* @brief Controller
*/
public $controller = 'core_activitystats_keywords';
public $controller = 'core_activitystats_keywords_time';
/**
* Render Chart
@@ -10,8 +10,22 @@
namespace IPS\core\extensions\core\Statistics;
use IPS\core\Statistics\Chart as ParentClass;
use IPS\Http\Url;
use IPS\Helpers\Chart;
use IPS\Helpers\Chart\Database;
use IPS\Member;
use IPS\Theme;
use IPS\DateTime as IPSDateTime;
use IPS\Db;
use function defined;
use function header;
use function count;
use function implode;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -20,7 +34,7 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Statistics Chart Extension
*/
class _Moderators extends \IPS\core\Statistics\Chart
class _Moderators extends ParentClass
{
/**
* @brief Controller
@@ -33,34 +47,42 @@ class _Moderators extends \IPS\core\Statistics\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
public function getChart( Url $url ): Chart
{
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_moderator_logs', 'ctime', '', array(
$chart = new 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' );
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'member_id', 'ctime' ), 'users' );
$chart->setExtension( $this );
$chart->groupBy = 'member_id';
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_moderator_activity_title');
$chart->title = Member::loggedIn()->language()->addToStack('stats_moderator_activity_title');
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
$chart->tableLangPrefix = 'mod_stats_';
$chart->tableInclude = array( 'member_id', 'lang_key', 'ctime' );
$chart->tableParsers = array(
'member_id' => function( $val ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinkWithPhoto( Member::load( $val ) );
},
'lang_key' => function( $val ) {
return Member::loggedIn()->language()->addToStack( $val . '_stats' );
},
'ctime' => function( $val )
{
return (string) \IPS\DateTime::ts( $val );
return (string) IPSDateTime::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 )
if ( $chart->start instanceof IPSDateTime or $chart->end instanceof IPSDateTime )
{
$start = $chart->start instanceof \IPS\DateTime ? $chart->start->getTimestamp() : 0;
$end = $chart->end instanceof \IPS\DateTime ? $chart->end->getTimestamp() : time();
$start = $chart->start instanceof IPSDateTime ? $chart->start->getTimestamp() : 0;
$end = $chart->end instanceof IPSDateTime ? $chart->end->getTimestamp() : time();
$where[] = array( 'ctime BETWEEN ? AND ?', $start, $end );
}
@@ -68,7 +90,7 @@ class _Moderators extends \IPS\core\Statistics\Chart
$modMember = [];
$modGroup = [];
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $mod )
foreach ( Db::i()->select( '*', 'core_moderators' ) as $mod )
{
if ( $mod['type'] == 'g' )
{
@@ -82,28 +104,28 @@ class _Moderators extends \IPS\core\Statistics\Chart
$query = [];
if ( \count( $modGroup ) )
if ( count( $modGroup ) )
{
$query[] = \IPS\Db::i()->in( 'member_group_id', $modGroup );
$query[] = \IPS\Db::i()->findInSet( 'mgroup_others', $modGroup );
$query[] = Db::i()->in( 'member_group_id', $modGroup );
$query[] = Db::i()->findInSet( 'mgroup_others', $modGroup );
}
if ( \count( $modMember ) )
if ( count( $modMember ) )
{
$query[] = \IPS\Db::i()->in( 'member_id', $modMember );
$query[] = 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 ) )
if ( count( $query ) )
{
$where[] = [ 'member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', implode( ' OR ', $query ) ) ];
$where[] = [ 'member_id IN(?)', 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' ) );
$topModerators = 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'] );
$member = Member::load( $moderatorRow['member_id'] );
$chart->addSeries(
( $member->member_id ) ? $member->name : \IPS\Member::loggedIn()->language()->addToStack( 'deleted_member' ),
'number',
@@ -0,0 +1,176 @@
<?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 20 Dec 2023
*/
namespace IPS\core\extensions\core\Statistics;
use IPS\core\Statistics\Chart as ParentClass;
use IPS\Helpers\Chart;
use IPS\Helpers\Chart\Database;
use IPS\Http\Url;
use IPS\DateTime as IPSDateTime;
use IPS\Member;
use IPS\Member\Group;
use IPS\Db;
use IPS\Helpers\Form\Select;
use IPS\Helpers\Form\Radio;
use IPS\Theme;
use DateInterval;
use function defined;
use function header;
use function array_values;
use function explode;
use function is_array;
use function count;
/* 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 _ModeratorsByAction extends ParentClass
{
/**
* @brief Controller
*/
public $controller = 'core_stats_moderators_actions';
/**
* Render Chart
*
* @param \IPS\Http\Url $url URL the chart is being shown on.
* @return \IPS\Helpers\Chart
*/
public function getChart( Url $url ): Chart
{
$chart = new 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' => IPSDateTime::create()->sub( new DateInterval( 'P90D' ) ), 'end' => IPSDateTime::create() ), array( 'lang_key', 'ctime' ), 'actions' );
$chart->setExtension( $this );
$chart->groupBy = 'lang_key';
$chart->title = Member::loggedIn()->language()->addToStack('stats_moderator_activity_title');
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
$chart->joins = array( array( 'core_members', "core_moderator_logs.member_id=core_members.member_id" ) );
$chart->tableInclude = array( 'member_id', 'lang_key', 'ctime' );
$chart->tableParsers = array(
'member_id' => function( $val, $row ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinKWithPhoto( Member::constructFromData( $row ) );
},
'lang_key' => function( $val ) {
return Member::loggedIn()->language()->addToStack( $val . '_stats' );
},
'ctime' => function( $val )
{
return (string) IPSDateTime::ts( $val );
}
);
$chart->tableLangPrefix = 'mod_stats_';
$customActionValues = ( isset( $chart->savedCustomFilters['chart_actions'] ) ) ? array_values( explode( ',', $chart->savedCustomFilters['chart_actions'] ) ) : -1;
$customGroupValues = ( isset( $chart->savedCustomFilters['chart_groups'] ) ) ? array_values( explode( ',', $chart->savedCustomFilters['chart_groups'] ) ) : -1;
$allActions = array();
foreach( Db::i()->select( 'DISTINCT(lang_key)', 'core_moderator_logs' ) AS $action )
{
$allActions[ $action ] = $action . '_stats';
}
$allGroups = array();
foreach( Group::groups( TRUE, FALSE ) AS $group )
{
$allGroups[ $group->g_id ] = $group->name;
}
$chart->customFiltersForm = array(
'form' => array(
new Select( 'chart_actions', $customActionValues, FALSE, array( 'options' => $allActions, 'multiple' => TRUE, 'noDefault' => TRUE, 'unlimited' => -1 ), NULL, NULL, NULL, 'chart_actions' ),
new Select( 'chart_groups', $customGroupValues, FALSE, array( 'options' => $allGroups, 'multiple' => TRUE, 'noDefault' => TRUE, 'unlimited' => -1 ), NULL, NULL, NULL, 'chart_groups' ),
),
'where' => function( $values )
{
$actions = array();
if ( $values['chart_actions'] != -1 )
{
$actions = is_array( $values['chart_actions'] ) ? array_values( $values['chart_actions'] ) : explode( ',', $values['chart_actions'] );
}
$groups = array();
if ( $values['chart_groups'] != -1 )
{
$groups = is_array( $values['chart_groups'] ) ? array_values( $values['chart_groups'] ) : explode( ',', $values['chart_groups'] );
}
$where = array();
if ( count( $actions ) )
{
$where[] = Db::i()->in( 'lang_key', $actions );
}
if ( count( $groups ) )
{
$where[] = Db::i()->in( 'core_members.member_group_id', $groups );
}
if ( count( $where ) )
{
return array( implode( ' AND ', $where ) );
}
else
{
return array();
}
},
'groupBy' => 'lang_key',
'series' => function( $values ) use ( $allActions )
{
$series = array();
if ( $values['chart_actions'] == -1 )
{
$custom = array_keys( $allActions );
}
else
{
$custom = is_array( $values['chart_actions'] ) ? array_values( $values['chart_actions'] ) : explode( ',', $values['chart_actions'] );
}
foreach( $custom as $key )
{
$series[] = array( Member::loggedIn()->language()->addToStack( $key . '_stats' ), 'number', 'COUNT(*)', FALSE, $key );
}
return $series;
},
'defaultSeries' => function() use ( $allActions )
{
$series = array();
foreach( $allActions as $key => $lang )
{
$series[] = array( Member::loggedIn()->language()->addToStack( $lang ), 'number', 'COUNT(*)', FALSE, $key );
}
return $series;
}
);
return $chart;
}
}
@@ -25,7 +25,7 @@ class _Points extends \IPS\core\Statistics\Chart
/**
* @brief Controller
*/
public $controller = NULL;
public $controller = 'core_stats_points';
/**
* Render Chart
@@ -35,7 +35,7 @@ class _Points extends \IPS\core\Statistics\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(
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_points_log', 'datetime', '', array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde' ),
@@ -25,7 +25,7 @@ class _Tags extends \IPS\core\Statistics\Chart
/**
* @brief Controller
*/
public $controller = NULL;
public $controller = 'core_activitystats_tags';
/**
* Render Chart
@@ -10,8 +10,20 @@
namespace IPS\core\extensions\core\Statistics;
use IPS\core\Statistics\Chart as ParentClass;
use IPS\Http\Url;
use IPS\Helpers\Chart;
use IPS\Helpers\Chart\Database;
use IPS\core\Warnings\Reason;
use IPS\Member;
use IPS\DateTime as IPSDateTime;
use IPS\Theme;
use function defined;
use function header;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -20,7 +32,7 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Statistics Chart Extension
*/
class _WarningReasons extends \IPS\core\Statistics\Chart
class _WarningReasons extends ParentClass
{
/**
* @brief Controller
@@ -33,9 +45,9 @@ class _WarningReasons extends \IPS\core\Statistics\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
public function getChart( Url $url ): Chart
{
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
$chart = new Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
@@ -46,19 +58,29 @@ class _WarningReasons extends \IPS\core\Statistics\Chart
$chart->groupBy = 'wl_reason';
foreach( \IPS\core\Warnings\Reason::roots() as $reason )
foreach( Reason::roots() as $reason )
{
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('core_warn_reason_' . $reason->id ), 'number', 'COUNT(*)', TRUE, $reason->id );
$chart->addSeries( 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->title = Member::loggedIn()->language()->addToStack('stats_warnings_title');
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
$chart->extension = $this;
$chart->tableInclude = array( 'wl_moderator', 'wl_member', 'wl_reason', 'wl_date' );
$chart->tableParsers = array(
'wl_moderator' => function( $val ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinkWithPhoto( Member::load( $val ) );
},
'wl_member' => function( $val ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinkWithPhoto( Member::load( $val ) );
},
'wl_reason' => function( $val ) {
return Member::loggedIn()->language()->addToStack( Reason::$titleLangPrefix . $val );
},
'wl_date' => function( $val )
{
return (string) \IPS\DateTime::ts( $val );
return (string) IPSDateTime::ts( $val );
}
);
@@ -10,8 +10,20 @@
namespace IPS\core\extensions\core\Statistics;
use IPS\core\Statistics\Chart as ParentClass;
use IPS\Http\Url;
use IPS\Helpers\Chart;
use IPS\Helpers\Chart\Database;
use IPS\core\Warnings\Reason;
use IPS\Member;
use IPS\DateTime as IPSDateTime;
use IPS\Theme;
use function defined;
use function header;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -20,7 +32,7 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Statistics Chart Extension
*/
class _WarningSuspended extends \IPS\core\Statistics\Chart
class _WarningSuspended extends ParentClass
{
/**
* @brief Controller
@@ -33,9 +45,9 @@ class _WarningSuspended extends \IPS\core\Statistics\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
public function getChart( Url $url ): Chart
{
$chart = new \IPS\Helpers\Chart\Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
$chart = new Database( $url, 'core_members_warn_logs', 'wl_date', '', array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
@@ -46,16 +58,26 @@ class _WarningSuspended extends \IPS\core\Statistics\Chart
$chart->where = array( 'wl_suspend IS NOT NULL' );
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('suspended' ), 'number', 'COUNT(*)', TRUE );
$chart->addSeries( Member::loggedIn()->language()->addToStack('suspended' ), 'number', 'COUNT(*)', TRUE );
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_warnings_title');
$chart->title = Member::loggedIn()->language()->addToStack('stats_suspended_title');
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
$chart->extension = $this;
$chart->tableInclude = array( 'wl_moderator', 'wl_member', 'wl_reason', 'wl_date' );
$chart->tableParsers = array(
'wl_moderator' => function( $val ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinkWithPhoto( Member::load( $val ) );
},
'wl_member' => function( $val ) {
return Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLinkWithPhoto( Member::load( $val ) );
},
'wl_reason' => function( $val ) {
return Member::loggedIn()->language()->addToStack( Reason::$titleLangPrefix . $val );
},
'wl_date' => function( $val )
{
return (string) \IPS\DateTime::ts( $val );
return (string) IPSDateTime::ts( $val );
}
);