Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

@@ -0,0 +1,162 @@
<?php
/**
* @brief automaticmoderation 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 08 Dec 2017
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* automaticmoderation Task
*/
class _automaticmoderation extends \IPS\Task
{
/**
* Execute
*
* If ran successfully, should return anything worth logging. Only log something
* worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases).
* If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log.
* Tasks should execute within the time of a normal HTTP request.
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
if ( ! \IPS\Settings::i()->automoderation_enabled )
{
return NULL;
}
foreach( \IPS\Db::i()->select( '*', 'core_automatic_moderation_pending', NULL, 'pending_added ASC', array( 0, 50 ) ) as $pending )
{
/* Check report exists still */
try
{
$report = \IPS\core\Reports\Report::load( $pending['pending_report_id'] );
}
catch( \OutofRangeException $e )
{
/* Report is missing */
\IPS\Db::i()->delete( 'core_automatic_moderation_pending', array( 'pending_report_id=?', $pending['pending_report_id'] ) );
continue;
}
/* Check object exists still */
$className = $pending['pending_object_class'];
try
{
$object = $className::load( $pending['pending_object_id'] );
}
catch( \OutofRangeException $e )
{
/* Object no longer exists */
\IPS\Db::i()->delete( 'core_automatic_moderation_pending', array( 'pending_object_class=? and pending_object_id=?', $pending['pending_object_class'], $pending['pending_object_id'] ) );
continue;
}
/* Check rule exists still */
try
{
$rule = \IPS\core\Reports\Rules::load( $pending['pending_rule_id'] );
}
catch( \OutofRangeException $e )
{
/* Rule no longer exists */
\IPS\Db::i()->delete( 'core_automatic_moderation_pending', array( 'pending_rule_id=?', $pending['pending_rule_id'] ) );
continue;
}
/* Right, that's all sorted so process the bad boy */
$hidden = FALSE;
try
{
if ( $object instanceof \IPS\Content\Comment )
{
$item = $object->item();
if ( $item and $item::$firstCommentRequired and $object->isFirst() )
{
/* Hide the item, not the object */
$item->hide( FALSE, 'automaticmoderation_hide_reason' );
$hidden = TRUE;
}
}
}
catch( \Exception $e ) { }
if ( $hidden === FALSE )
{
$object->hide( FALSE, 'automaticmoderation_hide_reason' );
}
/* Remove from the pending queue */
\IPS\Db::i()->delete( 'core_automatic_moderation_pending', array( 'pending_object_class=? and pending_object_id=?', $pending['pending_object_class'], $pending['pending_object_id'] ) );
/* Send notification to mods */
$moderators = array( 'm' => array(), 'g' => array() );
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $mod )
{
$canView = FALSE;
if ( $mod['perms'] == '*' )
{
$canView = TRUE;
}
if ( $canView === FALSE )
{
$perms = json_decode( $mod['perms'], TRUE );
if ( isset( $perms['can_view_reports'] ) AND $perms['can_view_reports'] === TRUE )
{
$canView = TRUE;
}
}
if ( $canView === TRUE )
{
$moderators[ $mod['type'] ][] = $mod['id'];
}
}
try
{
$latestReport = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=?', $report->id ), 'date_reported DESC' )->first();
$notification = new \IPS\Notification( \IPS\Application::load('core'), 'automatic_moderation', $report, array( $report, $latestReport, $object ) );
foreach ( \IPS\Db::i()->select( '*', 'core_members', ( count( $moderators['m'] ) ? \IPS\Db::i()->in( 'member_id', $moderators['m'] ) . ' OR ' : '' ) . \IPS\Db::i()->in( 'member_group_id', $moderators['g'] ) . ' OR ' . \IPS\Db::i()->findInSet( 'mgroup_others', $moderators['g'] ) ) as $member )
{
$notification->recipients->attach( \IPS\Member::constructFromData( $member ) );
}
$notification->send();
}
catch( \Exception $e ) { }
}
return NULL;
}
/**
* Cleanup
*
* If your task takes longer than 15 minutes to run, this method
* will be called before execute(). Use it to clean up anything which
* may not have been done
*
* @return void
*/
public function cleanup()
{
}
}
+19 -17
View File
@@ -33,6 +33,19 @@ class _cleanup extends \IPS\Task
/* Delete old password / security answer reset requests */
\IPS\Db::i()->delete( 'core_validating', array( '( lost_pass=1 OR forgot_security=1 ) AND email_sent < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P1D' ) )->getTimestamp() ) );
/* Delete cancelled registration members */
foreach( \IPS\Db::i()->select( 'member_id', 'core_validating', array( 'do_not_delete=0 and reg_cancelled > 0 and reg_cancelled < ?', time() - 86400 ) ) as $id )
{
try
{
\IPS\Member::load( $id )->delete();
}
catch ( \OutOfRangeException $e )
{
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=?', $id ) );
}
}
/* Delete old validating members */
if ( \IPS\Settings::i()->validate_day_prune )
{
@@ -93,12 +106,6 @@ class _cleanup extends \IPS\Task
\IPS\Db::i()->delete( 'core_error_logs', array( 'log_date < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->prune_log_error . 'D' ) )->getTimestamp() ) );
}
/* ...and system logs */
if( \IPS\Settings::i()->prune_log_system )
{
\IPS\Log::pruneLogs( \IPS\Settings::i()->prune_log_system );
}
/* ...and spam service logs */
if( \IPS\Settings::i()->prune_log_spam )
{
@@ -185,21 +192,11 @@ class _cleanup extends \IPS\Task
}
/* Delete any incompleted accounts (for example, someone has clicked "Sign in with Twitter" and never provided a username or email */
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array( array( '(name=? OR email=?)', '', '' ), array( 'joined<? AND last_visit=0 AND ipsconnect_id=0', \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() ) ) ), 'IPS\Member' ) as $incompleteMember )
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array( array( '(name=? OR email=?)', '', '' ), array( 'joined<? AND last_visit=0', \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() ) ) ), 'IPS\Member' ) as $incompleteMember )
{
$incompleteMember->delete();
}
/* Prune search index */
if ( \IPS\Settings::i()->search_index_timeframe )
{
try
{
\IPS\Content\Search\Index::i()->prune( \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->search_index_timeframe . 'D' ) ) );
}
catch ( \UnexpectedValueException $e ) { }
}
/* Remove widgets */
if ( \IPS\Application::appIsEnabled('cms') )
{
@@ -222,6 +219,11 @@ class _cleanup extends \IPS\Task
/* Close open polls that need closing */
\IPS\Db::i()->update( 'core_polls', array( 'poll_closed' => 1 ), array( 'poll_closed=? AND poll_close_date>? AND poll_close_date<?', 0, -1, time() ) );
/* Delete expired oAuth Authorization Codes */
\IPS\Db::i()->delete( 'core_oauth_server_authorization_codes', array( 'expires<?', time() ) );
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'refresh_token_expires<?', time() ) );
\IPS\Db::i()->delete( 'core_oauth_authorize_prompts', array( 'timestamp<?', ( time() - 300 ) ) );
return NULL;
}
+2 -1
View File
@@ -44,7 +44,8 @@ class _clearsessions extends \IPS\Task
}
\IPS\Db::i()->delete( 'core_sys_cp_sessions', array( 'session_running_time<?', ( time() - $timeout ) ) );
\IPS\Session\Front::clearSessions( $timeout );
$class = \IPS\Session\Store::i();
$class::clearSessions( $timeout );
return NULL;
}
-90
View File
@@ -1,90 +0,0 @@
<?php
/**
* @brief Reattempt queued IPS Connect requests
* @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 3 Dec 2013
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* Reattempt queued IPS Connect requests
*/
class _connect extends \IPS\Task
{
/**
* Execute
*
* @return mixed Message to log or NULL
* @throws \RuntimeException
*/
public function execute()
{
/* Get the next request */
try
{
$request = \IPS\Db::i()->select( 'q.*, s.*', array( 'core_ipsconnect_queue', 'q' ), array( 's.slave_enabled=?', 1 ), 'q.id asc', array( 0, 1 ) )
->join( array( 'core_ipsconnect_slaves', 's' ), "s.slave_id=q.slave_id" )
->first();
/* Try to process request again */
try
{
parse_str( $request['request_url'], $variables );
$response = \IPS\Http\Url::external( $request['slave_url'] )
->setQueryString( $variables )
->request()
->get();
if( $response->httpResponseCode !== NULL AND $response->httpResponseCode > 200 )
{
/* Update fail count */
\IPS\Db::i()->update( 'core_ipsconnect_queue', 'fail_count=fail_count+1', array( 'id=?', $request['id'] ) );
return NULL;
}
$response = $response->decodeJson();
/* If this is a registered slave but the slave is telling us Connect is disabled, remove it */
if( isset( $response['status'] ) AND $response['status'] == 'DISABLED' )
{
\IPS\Db::i()->update( 'core_ipsconnect_slaves', array( 'slave_enabled' => 0 ), array( 'slave_id=?', $request['slave_id'] ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => \IPS\Db::i()->select( 'COUNT(*)', 'core_ipsconnect_slaves', array( 'slave_enabled=?', 1 ) )->first() ), array( 'conf_key=?', 'connect_slaves' ) );
unset( \IPS\Data\Store::i()->settings );
}
/* If we are here, request succeeded - remove from queue */
\IPS\Db::i()->delete( 'core_ipsconnect_queue', array( 'id=?', $request['id'] ) );
return NULL;
}
catch( \RuntimeException $e )
{
/* Update fail count */
\IPS\Db::i()->update( 'core_ipsconnect_queue', 'fail_count=fail_count+1', array( 'id=?', $request['id'] ) );
return NULL;
}
}
catch( \UnderflowException $e )
{
/* There are none left - disable task */
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 0 ), "`key`='connect'" );
return NULL;
}
return NULL;
}
}
+12 -3
View File
@@ -47,9 +47,18 @@ class _deletecontent extends \IPS\Task
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_deletion_log', array( "dellog_deleted_date<?", $timeago->getTimestamp() ) ), 'IPS\core\DeletionLog' ) AS $log )
{
$class = $log->content_class;
$content = $class::load( $log->content_id );
$content->delete();
try
{
$class = $log->content_class;
if ( class_exists ( $class ) )
{
$content = $class::load( $log->content_id );
$content->delete();
}
}
/* If the content is gone already, don't let an uncaught exception bubble up...just remove the deletion log orphaned entry */
catch( \OutOfRangeException $e ){}
$log->delete();
}
+2 -2
View File
@@ -40,7 +40,7 @@ class _digest extends \IPS\Task
$frequency = $frequencies[ array_rand( $frequencies ) ];
/* Grab some members to send digests to. */
$members = \IPS\Db::i()->select( 'follow_member_id, follow_notify_sent', 'core_follow', array( 'follow_notify_do=1 AND follow_visible=1 AND follow_notify_freq = ? AND follow_notify_sent < ?', $frequency, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800 ), 'follow_notify_sent ASC', array( 0, 50 ), NULL, NULL, \IPS\Db::SELECT_DISTINCT );
$members = \IPS\Db::i()->select( 'follow_member_id, follow_notify_sent', 'core_follow', array( 'follow_notify_do=1 AND follow_notify_freq = ? AND follow_notify_sent < ?', $frequency, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800 ), 'follow_notify_sent ASC', array( 0, 50 ), NULL, NULL, \IPS\Db::SELECT_DISTINCT );
if( !count( $members ) )
{
@@ -55,7 +55,7 @@ class _digest extends \IPS\Task
}
/* Fetch the member's follows so we can build their digest */
$follows = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_notify_do=1 AND follow_notify_freq=? AND follow_visible=1 AND follow_notify_sent < ? AND ' . \IPS\Db::i()->in( 'follow_member_id', $memberIDs ), $frequency, time() - 86400 ), 'follow_notify_sent ASC, follow_app ASC, follow_area ASC' );
$follows = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_notify_do=1 AND follow_notify_freq=? AND follow_notify_sent < ? AND ' . \IPS\Db::i()->in( 'follow_member_id', $memberIDs ), $frequency, time() - 86400 ), 'follow_notify_sent ASC, follow_app ASC, follow_area ASC' );
$groupedFollows = array();
foreach( $follows as $follow )
+23 -10
View File
@@ -54,6 +54,13 @@ class _leaderboard extends \IPS\Task
else
{
$startDate = new \IPS\DateTime( 'yesterday midnight', $timezone );
/* Do we need to run this task at all? If we don't have any records in core_reputation_index we can skip this task */
$existing = \IPS\Db::i()->select( 'count(*)', 'core_reputation_index' )->first();
if ( !$existing )
{
return NULL;
}
}
$yesterdayEnd = new \IPS\DateTime( 'today midnight', $timezone );
@@ -65,19 +72,20 @@ class _leaderboard extends \IPS\Task
for( $i = 0; $i < $difference; $i++ )
{
$position = 0;
foreach( \IPS\Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', array( 'member_received > 0 AND rep_date BETWEEN ? and ?', $startDate->getTimeStamp(), $yesterdayEnd->getTimeStamp() ), 'rep DESC', 4, 'member' )->setKeyField('member')->setValueField('rep') as $member => $rep )
/* As this can run multiple times, and the unique constraint is member_id, position, it is possible to end up with multiple members with the same positon */
$date = $startDate->setTime( 12, 0 )->getTimeStamp();
\IPS\Db::i()->delete( 'core_reputation_leaderboard_history', array( 'leader_date BETWEEN ? and ?', $date - 4, $date ) );
foreach( \IPS\Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', array( 'member_received > 0 AND rep_date BETWEEN ? and ?', $startDate->setTime( 0, 0, 1 )->getTimeStamp(), $startDate->setTime( 23, 59, 59 )->getTimeStamp() ), 'rep DESC', 4, 'member' )->setKeyField('member')->setValueField('rep') as $member => $rep )
{
$date = $startDate->setTime( 12, 0 )->getTimeStamp();
$position++;
/* As this can run multiple times, and the unique constraint is member_id, position, it is possible to end up with multiple members with the same positon */
\IPS\Db::i()->delete( 'core_reputation_leaderboard_history', array( 'leader_date=? and leader_position=?', $date, $position ) );
if ( $member and $rep )
{
$date = $startDate->setTime( 12, 0 )->getTimeStamp();
$position++;
\IPS\Db::i()->replace( 'core_reputation_leaderboard_history', array(
'leader_date' => $startDate->setTime( 12, 0 )->getTimeStamp(),
'leader_date' => $date,
'leader_member_id' => $member,
'leader_position' => $position,
'leader_rep_total' => $rep
@@ -85,13 +93,18 @@ class _leaderboard extends \IPS\Task
}
}
/* If there are no entries, date is not defined correctly as midday */
if ( ! $position )
{
$date = $startDate->setTime( 12, 0 )->getTimeStamp();
}
/* Fill in the blanks */
if ( $position and $position < 5 )
{
while( $position < 4 )
{
$position++;
\IPS\Db::i()->delete( 'core_reputation_leaderboard_history', array( 'leader_date=? and leader_position=?', $date - $position, $position ) );
\IPS\Db::i()->replace( 'core_reputation_leaderboard_history', array(
'leader_date' => $date - $position,
+3 -2
View File
@@ -43,8 +43,9 @@ class _onlineusers extends \IPS\Task
try
{
$onlineGuests = \IPS\Db::i()->select( 'COUNT(*)', 'core_sessions', array( 'member_id=? OR member_id IS NULL', 0 ) )->first();
$onlineMembers = \IPS\Db::i()->select( 'COUNT(*)', 'core_sessions', array( 'member_id>?', 0 ), NULL, NULL, 'member_id' )->first();
/* @todo Right now, users excluded from the online user list would not be counted if using REDIS - Matt has said to leave it with him */
$onlineGuests = \IPS\Session\Store::i()->getOnlineUsers( \IPS\Session\Store::ONLINE_GUESTS | \IPS\Session\Store::ONLINE_COUNT_ONLY );
$onlineMembers = \IPS\Session\Store::i()->getOnlineUsers( \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_COUNT_ONLY, 'desc', NULL, NULL, TRUE );
}
catch( \UnderflowException $e) {}
+29 -15
View File
@@ -29,22 +29,36 @@ class _profilesync extends \IPS\Task
*/
public function execute()
{
$services = \IPS\core\ProfileSync\ProfileSyncAbstract::services();
if ( !empty( $services ) )
$totalToSync = \IPS\Db::i()->select( 'count(*)', 'core_members', array( "temp_ban=0 AND profilesync_lastsync > 0" ) )->first();
if( !$totalToSync )
{
$where = array( 'profilesync IS NOT NULL' );
/* Exclude banned users */
$where[] = array( 'temp_ban=?', 0 );
foreach ( \IPS\Db::i()->select( '*', 'core_members', $where, 'profilesync_lastsync ASC', 25 ) as $row )
{
foreach ( $services as $class )
{
$obj = new $class( \IPS\Member::constructFromData( $row ) );
$obj->sync();
}
}
return NULL;
}
else
{
/* The task runs once per hour and looks for all accounts that haven't been synced in 12+ hours, so we want to set a hard limit that
will allow all accounts to be processed in a 12 hour period if resources permit */
$accounts = ceil( $totalToSync / 12 );
}
$this->runUntilTimeout( function()
{
try
{
$member = \IPS\Db::i()->select( '*', 'core_members', array( "temp_ban=0 AND profilesync_lastsync > 0 AND profilesync_lastsync < ?", ( time() - ( 60 * 60 * 12 ) ) ), 'profilesync_lastsync ASC', 1 )->first();
}
catch ( \UnderflowException $e )
{
return FALSE;
}
$member = \IPS\Member::constructFromData( $member );
$member->profileSync();
return TRUE;
}, $accounts );
return NULL;
}
}
@@ -0,0 +1,64 @@
<?php
/**
* @brief prunesearchindex 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 07 Sep 2017
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* prunesearchindex Task
*/
class _prunesearchindex extends \IPS\Task
{
/**
* Execute
*
* If ran successfully, should return anything worth logging. Only log something
* worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases).
* If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log.
* Tasks should execute within the time of a normal HTTP request.
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
/* Prune search index */
if ( \IPS\Settings::i()->search_method == 'mysql' and \IPS\Settings::i()->search_index_timeframe )
{
try
{
\IPS\Content\Search\Index::i()->prune( \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->search_index_timeframe . 'D' ) ) );
}
catch ( \UnexpectedValueException $e ) { }
}
return NULL;
}
/**
* Cleanup
*
* If your task takes longer than 15 minutes to run, this method
* will be called before execute(). Use it to clean up anything which
* may not have been done
*
* @return void
*/
public function cleanup()
{
}
}
@@ -0,0 +1,59 @@
<?php
/**
* @brief prunesystemlogs 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 07 Sep 2017
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* prunesystemlogs Task
*/
class _prunesystemlogs extends \IPS\Task
{
/**
* Execute
*
* If ran successfully, should return anything worth logging. Only log something
* worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases).
* If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log.
* Tasks should execute within the time of a normal HTTP request.
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
if( \IPS\Settings::i()->prune_log_system )
{
\IPS\Log::pruneLogs( \IPS\Settings::i()->prune_log_system );
}
return NULL;
}
/**
* Cleanup
*
* If your task takes longer than 15 minutes to run, this method
* will be called before execute(). Use it to clean up anything which
* may not have been done
*
* @return void
*/
public function cleanup()
{
}
}
@@ -36,6 +36,7 @@ class _sitemapgenerator extends \IPS\Task
}
catch( \Exception $e )
{
\IPS\Log::log( $e, 'sitemap_generator' );
return $e->getMessage();
}
+9 -1
View File
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _updatecheck extends \IPS\Task
{
/**
* @brief Type to send to update server
*/
public $type = 'task';
/**
* Execute
*
@@ -29,6 +34,9 @@ class _updatecheck extends \IPS\Task
*/
public function execute()
{
/* Refresh stored license data */
\IPS\IPS::licenseKey( TRUE );
$fails = array();
/* Do IPS apps */
@@ -41,7 +49,7 @@ class _updatecheck extends \IPS\Task
}
}
$version = min( $versions );
$url = \IPS\Http\Url::ips('updateCheck');
$url = \IPS\Http\Url::ips('updateCheck')->setQueryString( array( 'type' => $this->type, 'key' => \IPS\Settings::i()->ipb_reg_number ) );
if ( \IPS\USE_DEVELOPMENT_BUILDS )
{
$url = $url->setQueryString( 'development', 1 );
+286
View File
@@ -0,0 +1,286 @@
<?php
/**
* @brief usagereporting 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 27 Dec 2017
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* usagereporting Task
*/
class _usagereporting extends \IPS\Task
{
/**
* Execute
*
* If ran successfully, should return anything worth logging. Only log something
* worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases).
* If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log.
* Tasks should execute within the time of a normal HTTP request.
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
/* Check usage reporting is enabled */
if ( !\IPS\Settings::i()->usage_reporting )
{
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 0 ), array( '`key`=?', 'usagereporting' ) );
return NULL;
}
/* Check this isn't a test install */
$licenseData = \IPS\IPS::licenseKey();
if ( \IPS\IN_DEV or !$licenseData or mb_substr( $licenseData['key'], -12 ) === '-TESTINSTALL' )
{
return NULL;
}
/* Send the report */
try
{
$response = \IPS\Http\Url::external('https://invisionpowerdiagnostics.com/usage')->request()->post( $this->buildReport() );
/* If the server has asked us to stop sending reports, then turn it off */
if ( $response->httpResponseCode == 410 )
{
\IPS\Settings::i()->changeValues( array( 'usage_reporting' => 0 ) );
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 0 ), array( '`key`=?', 'usagereporting' ) );
}
}
catch ( \IPS\Http\Url\Exception $e ) { }
return NULL;
}
/**
* Create report
*
* @return array
*/
final protected function buildReport()
{
/* Identifier which allows us to keep separate reports from this community
from other reports (so we can see how communities change things as they grow) but
does NOT allow us to identify which community is sending the report */
$report = array( 'anonymized_id' => md5( \IPS\Settings::i()->base_url . \IPS\Settings::i()->site_secret_key ) );
/* Community data */
$licenseData = \IPS\IPS::licenseKey(); // this is just to know if it is *active* and if it is CiC - we don't actually send the license key
$report['community'] = array(
'version' => \IPS\Application::load('core')->version,
'installed' => date( 'Y-m-d', \IPS\Settings::i()->board_start ),
'active_license'=> $licenseData ? ( (bool) $licenseData['active'] ) : FALSE,
'cloud' => $licenseData ? ( (bool) $licenseData['cloud'] ) : FALSE,
);
/* Server Data */
$report['server'] = array(
'php_version' => PHP_VERSION_ID,
'php_extensions' => get_loaded_extensions(),
'mysql_version' => \IPS\Db::i()->server_version,
'os' => PHP_OS,
);
/* Apps */
$report['apps'] = array();
foreach ( \IPS\Application::applications() as $app )
{
if ( $app->enabled )
{
$report['apps'][ $app->directory ] = $app->long_version;
}
}
/* Plugins */
$report['plugins'] = array();
foreach ( \IPS\Plugin::plugins() as $plugin )
{
if ( $plugin->enabled )
{
$report['plugins'][ $plugin->location ] = $plugin->version_long;
}
}
/* Themes */
$report['themes'] = array();
foreach ( \IPS\Theme::themes() as $theme )
{
$settings = array();
foreach ( $theme->settings as $k => $v )
{
if ( in_array( $k, array( 'responsive', 'rounded_photos', 'social_links', 'sidebar_position', 'sidebar_responsive', 'enable_fluid_width', 'fluid_width_size', 'js_incclude', 'ajax_pagination', 'cm_store_view', 'body_font', 'headline_font' ) ) )
{
$settings[ $k ] = $v;
}
}
$report['themes'][] = array(
'customised' => \IPS\Db::i()->select( 'COUNT(*)', 'core_theme_templates', array( 'template_set_id=?', $theme->id ) )->first(),
'settings' => $settings,
);
}
/* Languages */
$report['languages'] = array();
foreach ( \IPS\Lang::languages() as $lang )
{
$localeDot = \strpos( $lang->short, '.' );
$langKey = $localeDot ? \substr( $lang->short, 0, $localeDot ) : $lang->short;
if ( !isset( $report['languages'][ $langKey ] ) )
{
$report['languages'][ $langKey ] = 0;
}
$report['languages'][ $langKey ]++;
}
/* Settings */
foreach ( \IPS\Db::i()->select( array( 'conf_key', 'conf_value', 'conf_default', 'conf_app', 'conf_report' ), 'core_sys_conf_settings', 'conf_report IS NOT NULL' ) as $row )
{
if ( in_array( $row['conf_app'], \IPS\Application::$ipsApps ) and \IPS\Application::appIsEnabled( $row['conf_app'] ) )
{
if ( $row['conf_report'] == 'full' )
{
$report['settings'][ $row['conf_key'] ] = $row['conf_value'];
}
else
{
$report['settings'][ $row['conf_key'] ] = ( $row['conf_value'] != $row['conf_default'] );
}
}
}
/* Database counts */
foreach ( \IPS\Application::$ipsApps as $app )
{
if ( \IPS\Application::appIsEnabled( $app ) and file_exists( \IPS\ROOT_PATH . "/applications/{$app}/data/schema.json" ) )
{
foreach ( json_decode( file_get_contents( \IPS\ROOT_PATH . "/applications/{$app}/data/schema.json" ), TRUE ) as $table => $data )
{
if ( isset( $data['reporting'] ) and $data['reporting'] === 'count' )
{
$report['tables'][ $table ] = \IPS\Db::i()->select( 'COUNT(*)', $table )->first();
}
}
}
}
/* File storage configurations */
$report['files'] = array( 'amazon' => 0, 'database' => 0, 'filesystem' => 0, 'ftp' => 0, 'other' => 0 );
foreach ( \IPS\Application::allExtensions( 'core', 'FileStorage', FALSE ) as $k => $v )
{
try
{
$class = \strtolower( \substr( get_class( \IPS\File::getClass( $k ) ), 9 ) );
if ( isset( $report['files'][ $class ] ) )
{
$report['files'][ $class ]++;
}
else
{
$report['files']['other']++;
}
}
catch ( \Exception $e ) { }
}
/* Login methods */
$loginMethods = array(
'IPS\Login\Handler\Standard' => 'standard',
'IPS\Login\Handler\OAuth2\Facebook' => 'facebook',
'IPS\Login\Handler\OAuth2\Google' => 'google',
'IPS\Login\Handler\OAuth2\LinkedIn' => 'linkedin',
'IPS\Login\Handler\OAuth2\Microsoft' => 'microsoft',
'IPS\Login\Handler\OAuth1\Twitter' => 'twitter',
'IPS\Login\Handler\OAuth2\Invision' => 'invision',
'IPS\Login\Handler\OAuth2\Wordpress' => 'wordpress',
'IPS\Login\Handler\OAuth2\Custom' => 'oauth',
'IPS\Login\Handler\ExternalDatabase' => 'external',
'IPS\Login\Handler\LDAP' => 'ldap',
);
foreach ( $loginMethods as $k => $v )
{
$report['login'][ $v ] = 0;
}
$report['login']['other'] = 0;
foreach ( \IPS\Login::methods() as $method )
{
$class = get_class( $method );
if ( array_key_exists( $class, $loginMethods ) )
{
$report['login'][ $loginMethods[ $class ] ]++;
}
else
{
$report['login']['other']++;
}
}
/* Payment methods */
$report['paymethods'] = array();
if ( \IPS\Application::appIsEnabled('nexus') )
{
$report['paymethods'] = array( 'authorizenet_aim' => 0, 'authorizenet_dpm' => 0, 'manual' => 0, 'paypal_standard' => 0, 'paypal_pro' => 0, 'stripe_card' => 0, 'stripe_native' => 0, 'stripe_alipay' => 0, 'stripe_amex' => 0, 'stripe_bancontact' => 0, 'stripe_giropay' => 0, 'stripe_ideal' => 0, 'stripe_sofort' => 0, 'test' => 0, 'twocheckout' => 0, 'other' => 0 );
foreach ( \IPS\nexus\Gateway::roots() as $gateway )
{
$class = \strtolower( \substr( get_class( $gateway ), 18 ) );
$settings = json_decode( $gateway->settings, TRUE );
if ( $class === 'authorizenet' )
{
$class = ( $settings['method'] === 'AIM' ) ? 'authorizenet_aim' : 'authorizenet_dpm';
}
elseif ( $class === 'paypal' )
{
$class = ( $settings['type'] === 'paypal' ) ? 'paypal_standard' : 'paypal_pro';
}
elseif ( $class === 'stripe' )
{
$class = 'stripe_' . $settings['type'];
}
if ( array_key_exists( $class, $report['paymethods'] ) )
{
$report['paymethods'][ $class ]++;
}
else
{
$report['paymethods']['other']++;
}
}
}
/* Return */
return $report;
}
/**
* Cleanup
*
* If your task takes longer than 15 minutes to run, this method
* will be called before execute(). Use it to clean up anything which
* may not have been done
*
* @return void
*/
public function cleanup()
{
}
}
@@ -0,0 +1,90 @@
<?php
/**
* @brief User agent statistics 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 10 Jan 2018
*/
namespace IPS\core\tasks;
/* 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;
}
/**
* User agent statistics task
*/
class _userAgentStatistics extends \IPS\Task
{
/**
* Execute
*
* If ran successfully, should return anything worth logging. Only log something
* worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases).
* If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log.
* Tasks should execute within the time of a normal HTTP request.
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
$desktops = 0;
$tablets = 0;
$mobiles = 0;
$consoles = 0;
foreach( \IPS\Session\Store::i()->getOnlineUsers( \IPS\Session\Store::ONLINE_MEMBERS, 'desc', NULL, NULL, TRUE, TRUE ) as $row )
{
$userAgent = \IPS\Http\Useragent::parse( $row['browser'] );
if( in_array( $userAgent->platform, array( 'iPhone', 'Windows Phone OS', 'BlackBerry', 'Android', 'Tizen' ) ) )
{
$mobiles++;
}
elseif( in_array( $userAgent->platform, array( 'iPad / iPod Touch', 'Kindle', 'Kindle Fire', 'Playbook' ) ) )
{
$tablets++;
}
elseif( in_array( $userAgent->platform, array( 'Nintendo 3DS', 'New Nintendo 3DS', 'Nintendo Wii', 'Nintendo WiiU', 'PlayStation 3', 'PlayStation 4', 'PlayStation Vita', 'Xbox 360', 'Xbox One' ) ) )
{
$consoles++;
}
else
{
$desktops++;
}
}
\IPS\Db::i()->insert( 'core_statistics', array(
'type' => 'devices',
'value_1' => $mobiles,
'value_2' => $tablets,
'value_3' => $consoles,
'value_4' => $desktops,
'time' => time()
) );
return NULL;
}
/**
* Cleanup
*
* If your task takes longer than 15 minutes to run, this method
* will be called before execute(). Use it to clean up anything which
* may not have been done
*
* @return void
*/
public function cleanup()
{
}
}
+76 -32
View File
@@ -38,52 +38,96 @@ class _viewupdates extends \IPS\Task
$this->runUntilTimeout(function(){
try
{
$result = \IPS\Db::i()->select( 'classname, id, COUNT(*) AS count', 'core_view_updates', NULL, NULL, 20, array( 'classname', 'id' ) );
/* If there's no more, break the loop and stop */
if( !count( $result ) )
$database = \IPS\Db::i()->select( 'classname, id, COUNT(*) AS count', 'core_view_updates', NULL, NULL, 20, array( 'classname', 'id' ), NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER );
$redis = array();
/* Database results first */
foreach( $database as $row )
{
return FALSE;
}
foreach( $result as $row )
{
$class = $row['classname'];
if ( class_exists( $class ) and in_array( 'IPS\Content\Views', class_implements( $class ) ) AND isset( $class::$databaseColumnMap['views'] ) )
try
{
try
{
\IPS\Db::i()->update(
$class::$databaseTable,
"`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`=`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`+{$row['count']}",
array( "{$class::$databasePrefix}{$class::$databaseColumnId}=?", $row['id'] )
);
}
catch( \IPS\Db\Exception $e )
{
/* Table to update no longer exists */
if( $e->getCode() == 1146 )
{
\IPS\Db::i()->delete( 'core_view_updates', array( 'classname=?', $row['classname'] ) );
}
throw $e;
}
$this->update( $row['classname'], $row['id'], $row['count'] );
}
catch( \OutOfRangeException $e )
{
\IPS\Db::i()->delete( 'core_view_updates', array( 'classname=?', $row['classname'] ) );
}
\IPS\Db::i()->delete( 'core_view_updates', array( 'classname=? AND id=?', $row['classname'], $row['id'] ) );
}
}
catch ( \UnderflowException $e ) { }
/* Now try redis */
if ( \IPS\REDIS_ENABLED and \IPS\CACHE_METHOD == 'Redis' and \IPS\CACHE_CONFIG )
{
try
{
$redis = \IPS\Redis::i()->zRevRangeByScore( 'topic_views', '+inf', '-inf', array('withscores' => TRUE, 'limit' => array( 0, 20 ) ) );
/* If we're here we just ran some, so we can return true to see if there are any more to run */
foreach( $redis as $data => $count )
{
list( $class, $id ) = explode( '__', $data );
try
{
$this->update( $class, $id, intval( $count ) );
}
catch( \OutOfRangeException $e ) { }
\IPS\Redis::i()->zRem( 'topic_views', $data );
}
}
catch( \Exception $e ) { }
}
/* Go for another go? */
if ( count( $database ) or count( $redis ) )
{
return TRUE;
}
catch ( \UnderflowException $e )
else
{
return FALSE;
}
});
}
/**
* Update the row
*
* @param string $class Class to update
* @param int $id ID of item
* @param int $count Count to update
* @throws \OutOfRangeException When table to update no longer exists
*/
protected function update( $class, $id, $count )
{
if ( class_exists( $class ) and in_array( 'IPS\Content\Views', class_implements( $class ) ) AND isset( $class::$databaseColumnMap['views'] ) )
{
try
{
\IPS\Db::i()->update(
$class::$databaseTable,
"`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`=`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`+{$count}",
array( "{$class::$databasePrefix}{$class::$databaseColumnId}=?", $id )
);
\IPS\Content\Search\Index::i()->updateViewCounts( $class, $id, $count );
}
catch( \IPS\Db\Exception $e )
{
/* Table to update no longer exists */
if( $e->getCode() == 1146 )
{
throw new \OutOfRangeException;
}
throw $e;
}
}
}
/**
* Cleanup
*