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

+218 -15
View File
@@ -26,7 +26,14 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
* @brief Const No type selected
*/
const TYPE_MESSAGE = 0;
/**
* @brief Const Reported content statuses
*/
const STATUS_NEW = 1;
const STATUS_OPEN = 2;
const STATUS_CLOSED = 3;
const STATUS_REJECTED = 4;
/* !\IPS\Patterns\ActiveRecord */
@@ -141,7 +148,208 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
throw new \OutofRangeException;
}
}
/**
* Get all the report types for this report index
*
* @return array
*/
public function getReportedTypes(): array
{
$reportedTypes = [];
$reportTypes = [];
foreach ( \IPS\core\Reports\Types::roots() as $type )
{
$reportTypes[ $type->id ] = $type->_title;
}
foreach ( \IPS\Db::i()->select( '*', 'core_rc_reports', ['rid=? and report_type > 0', $this->id] ) as $report )
{
if ( isset( $reportTypes[ $report['report_type'] ] ) )
{
$reportedTypes[ $report['report_type'] ] = $reportTypes[ $report['report_type'] ];
}
}
return $reportedTypes;
}
/**
* Set the status of the report
*
* @param int $status
* @param $member
* @return void
*/
public function changeStatus( int $status, int $authorNotification=0, $member=null )
{
$member = $member ?: \IPS\Member::loggedIn();
$this->status = $status;
$this->save();
/* We only need to notify if there is an outcome */
if ( $status == static::STATUS_CLOSED or static::STATUS_REJECTED )
{
$forNotifications = [];
$forEmails = [];
/* Now go through all the individual reports and see if we need to notify anyone */
foreach ( \IPS\Db::i()->select( '*', 'core_rc_reports', ['rid=?', $this->id] ) as $report )
{
if ( !$report['report_type'] )
{
/* We have not selected a reason, no nothing to worry about */
continue;
}
/* Get the report type object */
try
{
$reportType = \IPS\core\Reports\Types::load( $report['report_type'] );
$reportMember = \IPS\Member::load( $report['report_by'] );
}
catch ( \Exception $e )
{
continue;
}
if ( $report['report_by'] )
{
/* We have a member */
$forNotifications[$report['report_type']][] = [$reportMember, $reportType, $report];
}
else
{
if ( $report['guest_email'] )
{
/* We have an email */
$forEmails[$report['report_type']][] = [$reportType, $report];
}
}
}
/* Members */
foreach ( $forNotifications as $typeId => $data )
{
foreach ( $data as $reportData )
{
$reportMember = $reportData[0];
$reportType = $reportData[1];
$userReport = $reportData[2];
if ( $this->getReportOutcomeBlurb( $userReport['id'] ) )
{
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'report_outcome', $this, [$this, $reportType, $userReport], ['reportType' => $reportType->id, 'userReport' => $userReport] );
$notification->recipients->attach( $reportMember );
$notification->send();
\IPS\Log::debug( "Sent notification to {$reportMember->name}", 'reports_notification' );
}
}
}
/* Guests */
foreach ( $forEmails as $typeId => $data )
{
foreach ( $data as $reportData )
{
$reportType = $reportData[0];
$guestEmail = $reportData[1]['guest_email'];
$userReport = $reportData[1];
if ( $guestEmail and $this->getReportOutcomeBlurb( $userReport['id'] ) )
{
\IPS\Email::buildFromTemplate( 'core', 'notification_report_outcome', [$this, $reportType, $userReport], \IPS\Email::TYPE_LIST )->send( $guestEmail );
}
}
}
/* Content author? */
if ( $authorNotification )
{
try
{
$class = $this->_data['class'];
$thing = $class::load( $this->_data['content_id'] );
$content = ( $thing instanceof \IPS\Content\Comment ) ? $thing->truncated() : $thing->mapped( 'title' );
if ( $thing->author() and $thing->author()->email )
{
$authorContent = static::getAuthorNotifications();
if ( isset( $authorContent[ $authorNotification ] ) )
{
$notificationContent = $authorContent[$authorNotification]['text'];
/* Substitutions */
$subs = [
'name' => $thing->author()->name,
'content' => trim( $content ),
'link' => (string) $thing->url(),
'reason' => implode( ', ', $this->getReportedTypes() )
];
foreach( $subs as $key => $value )
{
$notificationContent = str_replace( '{' . $key . '}', $value, $notificationContent );
}
\IPS\Email::buildFromTemplate( 'core', 'notification_report_content_author', [$this, $thing, $notificationContent], \IPS\Email::TYPE_LIST )->send( $thing->author()->email );
}
}
}
catch ( \Exception $e ) { }
}
}
/* Post a comment on the report */
$content = $member->language()->addToStack( 'update_report_status_content', FALSE, array( 'sprintf' => array( $member->language()->addToStack( 'report_status_' . $this->status ) ) ) );
$member->language()->parseOutputForDisplay( $content );
$comment = \IPS\core\Reports\Comment::create( $this, $content, TRUE, NULL, NULL, $member, new \IPS\DateTime );
$comment->save();
/* And add to the moderator log */
\IPS\Session::i()->modLog( 'modlog__action_update_report_status', array( $this->url()->__toString() => FALSE ) );
}
/**
* Get the author notifications
*
* @return array
*/
public static function getAuthorNotifications(): array
{
$return = [];
foreach( \IPS\Db::i()->select( '*', 'core_rc_author_notification_text', [], 'title ASC' ) as $row )
{
$return[ $row['id'] ] = $row;
}
return $return;
}
/**
* Get the report outcome blurb
*
* @param int $userReportId
* @return string
*/
public function getReportOutcomeBlurb( int $userReportId ): string
{
try
{
$userReport = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'id=?', $userReportId ) )->first();
$reportType = \IPS\core\Reports\Types::load( $userReport['report_type'] );
return $this->status === static::STATUS_REJECTED ? $reportType->getRejectedNotificationText( \IPS\Member::load( $userReport['report_by'] ) ) : $reportType->getCompletedNotificationText( \IPS\Member::load( $userReport['report_by'] ) );
}
catch( \Exception $e )
{
return '';
}
}
/**
* Get mapped value
*
@@ -359,18 +567,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
{
if ( mb_substr( $action, 0, -1 ) === 'report_status_' )
{
$this->status = mb_substr( $action, -1 );
$this->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_' . $this->status ) ) ) );
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );
$comment = \IPS\core\Reports\Comment::create( $this, $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( $this->url()->__toString() => FALSE ) );
$this->changeStatus( (int) mb_substr( $action, -1 ) );
}
else
{
@@ -385,7 +582,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
*/
public function customMultimodActions()
{
return array_diff( array( 'report_status_1', 'report_status_2', 'report_status_3', ), array( 'report_status_' . $this->status ) );
return array_diff( array( 'report_status_1', 'report_status_2', 'report_status_3', 'report_status_4'), array( 'report_status_' . $this->status ) );
}
/**
@@ -416,6 +613,11 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
'action' => 'report_status_3',
'icon' => 'check-circle',
'label' => 'report_status_3'
),
array(
'action' => 'report_status_4',
'icon' => 'archive',
'label' => 'report_status_4'
)
)
)
@@ -574,6 +776,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
$typeCounts = array();
$total = 0;
$seen = array();
/* @note SELECT_FROM_WRITE_SERVER added in 7fd46ad949b0a9b03a5c67c70afee26184c2930c pull #316 (The problem was, that the R/W separation.The new report was added and in the same step we fetched(counted) the existing reports, where the recent one wasn't taken into account.) */
foreach( \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? and report_type > 0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER ) as $row )
{
if ( isset( $seen[ $row['report_by'] ] ) )
+20 -10
View File
@@ -86,12 +86,14 @@ class _Rules extends \IPS\Node\Model
$form->add( new \IPS\Helpers\Form\Number( 'automaticmoderation_points', $this->id ? $this->points_needed : 10, TRUE, array( 'min' => 1, 'max' => 9999 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('automaticmoderation_points_suffix') ) );
$options = array();
foreach( \IPS\core\Reports\Types::roots() as $type )
if( count( \IPS\core\Reports\Types::roots() ) > 0 )
{
$options[ $type->id ] = $type->_title;
foreach( \IPS\core\Reports\Types::roots() as $type )
{
$options[ $type->id ] = $type->_title;
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'automaticmoderation_type', $this->types ? explode( ',', $this->types ) : array(), TRUE, array( 'options' => $options ) ) );
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'automaticmoderation_type', $this->types ? explode( ',', $this->types ) : array(), TRUE, array( 'options' => $options ) ) );
/* Loop over our member filters */
$form->addHeader( 'automaticmoderation_generic_filters' );
@@ -183,7 +185,11 @@ class _Rules extends \IPS\Node\Model
$values['rule_filters'] = json_encode( $_options );
$values['rule_points_needed'] = $values['automaticmoderation_points'];
$values['rule_enabled'] = $values['automaticmoderation_enabled'];
$values['rule_types'] = $values['automaticmoderation_type'];
if( isset( $values['automaticmoderation_type'] ) )
{
$values['rule_types'] = $values['automaticmoderation_type'];
}
/* Now we have to remove any fields that aren't valid... */
foreach( $values as $k => $v )
@@ -360,17 +366,21 @@ class _Rules extends \IPS\Node\Model
/* Loop over the filters */
$descriptions = array();
foreach( $this->_filters as $key => $filter )
if( $this->_filters )
{
if( isset( static::$extensions[ $key ] ) )
foreach( $this->_filters as $key => $filter )
{
/* Ask the extension if this member matches the defined rule...if not, just return FALSE now */
if( method_exists( static::$extensions[ $key ], 'getDescription' ) and $description = static::$extensions[ $key ]->getDescription( $filter ) )
if( isset( static::$extensions[ $key ] ) )
{
$descriptions[] = $description; /* Is this descriptive enough? */
/* Ask the extension if this member matches the defined rule...if not, just return FALSE now */
if( method_exists( static::$extensions[ $key ], 'getDescription' ) and $description = static::$extensions[ $key ]->getDescription( $filter ) )
{
$descriptions[] = $description; /* Is this descriptive enough? */
}
}
}
}
return \IPS\Member::loggedIn()->language()->addToStack( 'automaticmoderation_row_desc', TRUE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $descriptions ) ) ) );
}
+33 -2
View File
@@ -11,6 +11,9 @@
namespace IPS\core\Reports;
/* 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' );
@@ -66,7 +69,29 @@ class _Types extends \IPS\Node\Model
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
*/
public static $titleLangPrefix = 'automaticmod_types_';
/**
* Get the completed notification text (hits the DB to get the real value, not the hash key)
* @param Member|null $member
* @return string
*/
public function getCompletedNotificationText( \IPS\Member|null $member=null ): string
{
$member = $member ?: \IPS\Member::loggedIn();
return $member->language()->get( 'automaticmod_types_notify_complete_' . $this->id, 'core' );
}
/**
* Get the rejected notification text (hits the DB to get the real value, not the hash key)
* @param Member|null $member
* @return string
*/
public function getRejectedNotificationText( \IPS\Member|null $member=null ): string
{
$member = $member ?: \IPS\Member::loggedIn();
return $member->language()->get( 'automaticmod_types_notify_reject_' . $this->id, 'core' );
}
/**
* Form
*
@@ -76,6 +101,8 @@ class _Types extends \IPS\Node\Model
public function form( &$form )
{
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_title', NULL, TRUE, array( 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_' . $this->id : NULL ) ) ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_notify_complete', NULL, FALSE, array( 'textArea' => true, 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_notify_complete_' . $this->id : NULL ) ) ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_notify_reject', NULL, FALSE, array( 'textArea' => true, 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_notify_reject_' . $this->id : NULL ) ) ) );
}
/**
@@ -93,8 +120,12 @@ class _Types extends \IPS\Node\Model
/* Save the title */
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_' . $this->id, $values['automaticmod_types_title'] );
/* Save the notification text */
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_notify_complete_' . $this->id, $values['automaticmod_types_notify_complete'] );
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_notify_reject_' . $this->id, $values['automaticmod_types_notify_reject'] );
unset( $values['automaticmod_types_title'] );
unset( $values['automaticmod_types_title'], $values['automaticmod_types_notify_complete'], $values['automaticmod_types_notify_reject'] );
return parent::formatFormValues( $values );
}