Version 4.7.6

This commit is contained in:
Neo committed 2025-12-19 16:05:19 -08:00
1 parent 8ba7d43898
commit 7b7404e741
1085 files changed
+30590 -7837

No files matched your search

+1 -1
View File
@@ -107,7 +107,7 @@ class _archive extends \IPS\Task
catch ( \UnderflowException $e ) { }
/* Can we do even more? */
$query = array_merge( array( array( 'topic_archive_status=?', \IPS\forums\Topic::ARCHIVE_NOT ) ), \IPS\Application::load('forums')->archiveWhere( \IPS\Db::i()->select( '*', 'forums_archive_rules' ) ) );
$query = array_merge( array( array( 'topic_archive_status=? and approved !=?', \IPS\forums\Topic::ARCHIVE_NOT, -2 ) ), \IPS\Application::load('forums')->archiveWhere( \IPS\Db::i()->select( '*', 'forums_archive_rules' ) ) );
if ( $postsProcessed < static::PROCESS_PER_BATCH )
{
/* Loop topics */
@@ -0,0 +1,128 @@
<?php
/**
* @brief solvedNotifications 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
* @subpackage forums
* @since 15 Jul 2022
*/
namespace IPS\forums\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;
}
/**
* solvedNotifications Task
*/
class _solvedNotifications 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()->forums_solved_topic_reengage )
{
return NULL;
}
/* Get forums with solved mode where the member can mark stuff as solved but have a hard line at 1 year ago as the oldest to ask about. We will need to watch this query. If it proves to be
super inefficient with sites with millions of members, we can consider an index on members_bitoptions2 or remove that sub-query and just check the author in the code before sending
which would suck if all 50 were for members with re-enagement emails off, but we will see */
$where = [
[ \IPS\Db::i()->in( 'approved', array( -2, -3 ), TRUE ) ],
[ 'posts > 1 and start_date > ? and start_date < ? and solved_reminder_sent=0 and topic_answered_pid=0', ( time() - ( 365 * 86400) ), ( time() - ( (int) \IPS\Settings::i()->forums_solved_topic_reengage * 86400 ) ) ],
[ \IPS\Db::i()->in( 'forum_id', iterator_to_array( \IPS\Db::i()->select( 'id', 'forums_forums', '(' . \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers_member' ) . ')' ) ) ) ],
[ 'starter_id NOT IN (?)', \IPS\Db::i()->select( 'member_id', 'core_members', \IPS\Db::i()->bitwiseWhere( \IPS\Member::$bitOptions['members_bitoptions'], 'no_solved_reenage' ) ) ]
];
$topics = [];
foreach( \IPS\Db::i()->select( '*', 'forums_topics', $where, 'start_date ASC', [ 0, 50 ] ) as $row )
{
$topics[ $row['tid'] ] = $row;
}
$starters = [];
if ( \count( $topics ) )
{
/* Get a count of how many reminders have been sent already */
foreach( \IPS\Db::i()->select( 'starter_id, COUNT(*) as count', 'forums_topics', [
[ 'solved_reminder_sent > ?', time() - ( 86400 * 7 ) ]
], NULL, NULL, 'starter_id' ) as $row ) {
$starters[ $row['starter_id'] ] = $row['count'];
}
foreach( $topics as $row )
{
if ( ! isset( $starters[ $row['starter_id'] ] ) )
{
$starters[ $row['starter_id'] ] = 0;
}
if ( $starters[ $row['starter_id'] ] >= 5 )
{
/*
We don't want to send this one as the starter has had too many sent, so reset time to 5 days ago so a task can check again on this one in a few days
There is hardcoded to 5 max a week. We could consider a setting but I feel that 5 max per week isn't overwhelming but is enough to prompt re-engagement
*/
\IPS\Db::i()->update( 'forums_topics', [ 'solved_reminder_sent' => time() - ( 86400 * 5 ) ], ['tid=?', $row['tid']] );
}
else
{
/* Avoid race conditions and make sure its updated even if constructFromData fails */
\IPS\Db::i()->update( 'forums_topics', [ 'solved_reminder_sent' => time() ], ['tid=?', $row['tid']] );
/* Increment so the next foreach will count it correctly */
$starters[ $row['starter_id'] ]++;
try
{
$topic = \IPS\forums\Topic::constructFromData( $row );
if ( $topic->canView( $topic->author() ) )
{
$replies = $topic->comments( 4, 0, 'date', 'asc' );
$firstPost = array_shift( $replies );
\IPS\Email::buildFromTemplate( 'core', 'solved_reengagement', array($topic, $firstPost, $replies), \IPS\Email::TYPE_TRANSACTIONAL )->send( $topic->author() );
}
}
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()
{
}
}
@@ -0,0 +1,91 @@
<?php
/**
* @brief solvedStatsAggregation 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
* @subpackage forums
* @since 25 Jul 2022
*/
namespace IPS\forums\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;
}
/**
* solvedStatsAggregation Task
*/
class _solvedStatsAggregation extends \IPS\Task
{
/**
* Aggregate yesterday's (server time 0:00:00 to 23:59:59) solve stats per forum. Depending when the task runs, it is possible for it to run multiple
* times in a day, so make sure we remove any previous entries. I've put the data in core_statistics but if this becomes too large to manage, we can
* create a new table for it. Ostensibly this is a core feature, but really our focus is on forums to use the solve feature.
*
* core_statistics mapping:
* type: solved
* value_1: forum_id
* value_2: total topics added
* value_3: total solved
* value_4: AVG time to solved (in seconds)
* time: timestamp of the start of the day (so 0:00:00)
*
* @return mixed Message to log or NULL
* @throws \IPS\Task\Exception
*/
public function execute()
{
$timezone = new \DateTimeZone( \IPS\Settings::i()->reputation_timezone );
$end = \IPS\DateTime::create()->setTimezone( $timezone )->sub( new \DateInterval( 'P1D' ) )->setTime( 23, 59 );
/* Now to iterate over all forums that have solved enabled and crunch the statistics */
foreach( \IPS\Db::i()->select( '*', 'forums_forums', [ '(' . \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers' ) . ') OR ( ' . \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers_moderator' ) . ' )' ] ) as $forum )
{
$start = \IPS\DateTime::ts( \IPS\forums\Forum::constructFromData( $forum )->getFirstSolvedTime() )->setTimezone( $timezone );
$where = [
[ \IPS\Db::i()->in( 'state', array( 'link', 'merged' ), TRUE ) ],
[ \IPS\Db::i()->in( 'approved', array( -2, -3 ), TRUE ) ],
[ 'forum_id=?', $forum['id'] ],
[ 'start_date > ? AND start_date < ?', $start->getTimestamp(), $end->getTimestamp() ]
];
$total = \IPS\Db::i()->select( 'COUNT(*)', 'forums_topics', $where )->first();
$solved = \IPS\Db::i()->select( 'COUNT(*)', 'forums_topics', array_merge( $where, [ [ 'core_solved_index.id IS NOT NULL' ] ] ) )->join( 'core_solved_index', "core_solved_index.app='forums' AND core_solved_index.item_id=forums_topics.tid")->first();
$avg = \IPS\Db::i()->select( 'AVG(core_solved_index.solved_date-forums_topics.start_date)', 'forums_topics', array_merge( $where, [ [ 'core_solved_index.id IS NOT NULL' ] ] ) )->join( 'core_solved_index', "core_solved_index.app='forums' AND core_solved_index.item_id=forums_topics.tid")->first();
\IPS\Db::i()->delete( 'core_statistics', [ 'type=? and time=? and value_1=?', 'solved', $end->getTimestamp(), $forum['id'] ] );
\IPS\Db::i()->insert( 'core_statistics', [
'type' => 'solved',
'value_1' => $forum['id'],
'value_2' => $total,
'value_3' => $solved,
'value_4' => $avg,
'time' => $end->getTimestamp()
] );
}
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()
{
}
}