Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
@@ -11,41 +11,60 @@
|
||||
namespace IPS\core\Digest;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use BadMethodCallException;
|
||||
use IPS\Application;
|
||||
use IPS\Content;
|
||||
use IPS\Content\Filter;
|
||||
use IPS\Content\Tag;
|
||||
use IPS\Content\Taggable;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Email;
|
||||
use IPS\IPS;
|
||||
use IPS\Log;
|
||||
use IPS\Member;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Digest Class
|
||||
*/
|
||||
class _Digest
|
||||
class Digest
|
||||
{
|
||||
/**
|
||||
* @brief [IPS\Member] Digest member object
|
||||
*/
|
||||
public $member = NULL;
|
||||
public ?Member $member = NULL;
|
||||
|
||||
/**
|
||||
* @brief Output to include in digest email template
|
||||
*/
|
||||
public $output = array( 'html' => '', 'plain' => '' );
|
||||
public array $output = array( 'html' => '', 'plain' => '' );
|
||||
|
||||
/**
|
||||
* @brief Frequency Daily/Weekly
|
||||
*/
|
||||
public $frequency = NULL;
|
||||
public ?string $frequency = NULL;
|
||||
|
||||
/**
|
||||
* @brief Is there anything to send?
|
||||
*/
|
||||
public $hasContent = FALSE;
|
||||
public bool $hasContent = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Mail Object
|
||||
*/
|
||||
protected $mail;
|
||||
protected ?Email $mail;
|
||||
|
||||
/**
|
||||
* Build Digest
|
||||
@@ -53,7 +72,7 @@ class _Digest
|
||||
* @param array $data Array of follow records
|
||||
* @return void
|
||||
*/
|
||||
public function build( $data )
|
||||
public function build( array $data ) : void
|
||||
{
|
||||
/* Banned members should not be emailed */
|
||||
if( $this->member->isBanned() )
|
||||
@@ -69,7 +88,7 @@ class _Digest
|
||||
|
||||
/* We just do it this way because for backwards-compatibility, template parsing expects an \IPS\Email object with a $language property
|
||||
This email is never actually sent and a new one is generated in send() */
|
||||
$this->mail = \IPS\Email::buildFromTemplate( 'core', 'digest', array( $this->member, $this->frequency ), \IPS\Email::TYPE_LIST );
|
||||
$this->mail = Email::buildFromTemplate( 'core', 'digest', array( $this->member, $this->frequency ), Email::TYPE_LIST );
|
||||
$this->mail->language = $this->member->language();
|
||||
|
||||
$numberOfItems = 0;
|
||||
@@ -77,7 +96,7 @@ class _Digest
|
||||
{
|
||||
foreach ( $area as $items )
|
||||
{
|
||||
$numberOfItems += \count( $items );
|
||||
$numberOfItems += count( $items );
|
||||
}
|
||||
}
|
||||
$max = ceil( 80 / $numberOfItems );
|
||||
@@ -86,20 +105,91 @@ class _Digest
|
||||
{
|
||||
foreach( $area as $key => $follows )
|
||||
{
|
||||
if( $key == 'tag' )
|
||||
{
|
||||
foreach( $follows as $follow )
|
||||
{
|
||||
try
|
||||
{
|
||||
$tag = Tag::load( $follow['follow_rel_id'] );
|
||||
}
|
||||
catch( OutOfRangeException )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$areaPlainOutput = NULL;
|
||||
$areaHtmlOutput = NULL;
|
||||
$added = FALSE;
|
||||
$header = sprintf( $this->member->language()->get( 'digest_area_core_tag' ), $tag->text );
|
||||
|
||||
foreach( Db::i()->select( 't.*', [ 'core_tags', 't' ], [
|
||||
[ 't.tag_member_id!=?', $this->member->member_id ],
|
||||
[ 't.tag_added > ?', ( $follow['follow_notify_sent'] ?: $follow['follow_added'] ) ],
|
||||
[ 't.tag_text=?', $tag->text ],
|
||||
[ 'p.tag_perm_visible=?', 1 ],
|
||||
[ '(p.tag_perm_text=? OR ' . Db::i()->findInSet( 'p.tag_perm_text', $this->member->groups ) . ')', '*' ]
|
||||
], 't.tag_added desc', $max )->join( [ 'core_tags_perms', 'p' ], 't.tag_aai_lookup=p.tag_perm_aai_lookup and t.tag_aap_lookup=p.tag_perm_aap_lookup' ) as $row )
|
||||
{
|
||||
/* Check the application first */
|
||||
if( Application::appIsEnabled( $row['tag_meta_app'] ) )
|
||||
{
|
||||
/* Figure out which class this is */
|
||||
$class = null;
|
||||
foreach( Content::routedClasses( false, false, true ) as $itemClass )
|
||||
{
|
||||
if( IPS::classUsesTrait( $itemClass, Taggable::class ) and $itemClass::$application == $row['tag_meta_app'] and $itemClass::$module == $row['tag_meta_area'] )
|
||||
{
|
||||
$class = $itemClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( $class !== null )
|
||||
{
|
||||
try
|
||||
{
|
||||
$item = $class::load( $row['tag_meta_id'] );
|
||||
$areaPlainOutput .= Email::template( $row['tag_meta_app'], 'digests__item', 'plaintext', array( $item, $this->mail ) );
|
||||
$areaHtmlOutput .= Email::template( $row['tag_meta_app'], 'digests__item', 'html', array( $item, $this->mail ) );
|
||||
|
||||
$added = TRUE;
|
||||
++$count;
|
||||
}
|
||||
catch( OutOfRangeException ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wrapper */
|
||||
if( $added )
|
||||
{
|
||||
$this->output['plain'] .= Email::template( 'core', 'digests__areaWrapper', 'plaintext', array( $areaPlainOutput, $app, $key, $max, $count, $header, $this->mail ) );
|
||||
$this->output['html'] .= Email::template( 'core', 'digests__areaWrapper', 'html', array( $areaHtmlOutput, $app, $key, $max, $count, $header, $this->mail ) );
|
||||
|
||||
$this->hasContent = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
|
||||
|
||||
$areaPlainOutput = NULL;
|
||||
$areaHtmlOutput = NULL;
|
||||
$added = FALSE;
|
||||
$header = NULL;
|
||||
|
||||
/* Following an item or node */
|
||||
$class = 'IPS\\' . $app . '\\' . mb_ucfirst( $key );
|
||||
$class = 'IPS\\' . $app . '\\' . IPS::mb_ucfirst( $key );
|
||||
|
||||
if ( class_exists( $class ) AND \IPS\Application::appIsEnabled( $app ) )
|
||||
if ( class_exists( $class ) AND Application::appIsEnabled( $app ) )
|
||||
{
|
||||
$parents = class_parents( $class );
|
||||
|
||||
if ( \in_array( 'IPS\Node\Model', $parents ) )
|
||||
|
||||
if ( in_array( 'IPS\Node\Model', $parents ) )
|
||||
{
|
||||
foreach ( $follows as $follow )
|
||||
{
|
||||
@@ -115,6 +205,7 @@ class _Digest
|
||||
$commentClass::$joinProfileFields = FALSE;
|
||||
}
|
||||
|
||||
/* @var array $databaseColumnMap */
|
||||
$where = array(
|
||||
array( $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['container'] . '=? AND ' . $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['date'] . ' > ? AND ' . $itemClass::$databaseTable . '.' .$itemClass::$databasePrefix . $itemClass::$databaseColumnMap['author'] . '!=?',
|
||||
$follow['follow_rel_id'],
|
||||
@@ -127,27 +218,26 @@ class _Digest
|
||||
$itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['date'] . ' ASC',
|
||||
$max,
|
||||
'read',
|
||||
\IPS\Content\Hideable::FILTER_OWN_HIDDEN,
|
||||
NULL,
|
||||
Filter::FILTER_OWN_HIDDEN,
|
||||
0,
|
||||
$this->member,
|
||||
TRUE
|
||||
) as $item )
|
||||
{
|
||||
try
|
||||
{
|
||||
$areaPlainOutput .= \IPS\Email::template( $app, 'digests__item', 'plaintext', array( $item, $this->mail ) );
|
||||
$areaHtmlOutput .= \IPS\Email::template( $app, 'digests__item', 'html', array( $item, $this->mail ) );
|
||||
$areaPlainOutput .= Email::template( $app, 'digests__item', 'plaintext', array( $item, $this->mail ) );
|
||||
$areaHtmlOutput .= Email::template( $app, 'digests__item', 'html', array( $item, $this->mail ) );
|
||||
|
||||
$added = TRUE;
|
||||
++$count;
|
||||
}
|
||||
catch ( \BadMethodCallException $e ) {}
|
||||
catch ( \UnderflowException $e ) {}
|
||||
catch ( BadMethodCallException | UnderflowException $e ) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( \in_array( 'IPS\Content\Item', $parents ) )
|
||||
else if ( in_array( 'IPS\Content\Item', $parents ) )
|
||||
{
|
||||
foreach ( $follows as $follow )
|
||||
{
|
||||
@@ -178,26 +268,26 @@ class _Digest
|
||||
$commentClass::$joinProfileFields = FALSE;
|
||||
}
|
||||
|
||||
foreach( $item->comments( 5, NULL, 'date', 'asc', NULL, FALSE, \IPS\DateTime::ts( $follow['follow_notify_sent'] ?: $follow['follow_added'] ), NULL, FALSE, FALSE, FALSE ) as $comment )
|
||||
foreach( $item->comments( 5, NULL, 'date', 'asc', NULL, FALSE, DateTime::ts( $follow['follow_notify_sent'] ?: $follow['follow_added'] ), NULL, FALSE, FALSE, FALSE ) as $comment )
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
$areaPlainOutput .= \IPS\Email::template( $app, 'digests__comment', 'plaintext', array( $comment, $this->mail ) );
|
||||
$areaHtmlOutput .= \IPS\Email::template( $app, 'digests__comment', 'html', array( $comment, $this->mail ) );
|
||||
$areaPlainOutput .= Email::template( $app, 'digests__comment', 'plaintext', array( $comment, $this->mail ) );
|
||||
$areaHtmlOutput .= Email::template( $app, 'digests__comment', 'html', array( $comment, $this->mail ) );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch ( UnderflowException $e )
|
||||
{
|
||||
/* If an app forgot digest templates, we don't want the entire task to fail to ever run again */
|
||||
\IPS\Log::debug( $e, 'digestBuild' );
|
||||
throw new \OutOfRangeException;
|
||||
Log::debug( $e, 'digestBuild' );
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
|
||||
$added = TRUE;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -206,8 +296,8 @@ class _Digest
|
||||
/* Wrapper */
|
||||
if( $added )
|
||||
{
|
||||
$this->output['plain'] .= \IPS\Email::template( 'core', 'digests__areaWrapper', 'plaintext', array( $areaPlainOutput, $app, $key, $max, $count, $this->mail ) );
|
||||
$this->output['html'] .= \IPS\Email::template( 'core', 'digests__areaWrapper', 'html', array( $areaHtmlOutput, $app, $key, $max, $count, $this->mail ) );
|
||||
$this->output['plain'] .= Email::template( 'core', 'digests__areaWrapper', 'plaintext', array( $areaPlainOutput, $app, $key, $max, $count, $header, $this->mail ) );
|
||||
$this->output['html'] .= Email::template( 'core', 'digests__areaWrapper', 'html', array( $areaHtmlOutput, $app, $key, $max, $count, $header, $this->mail ) );
|
||||
|
||||
$this->hasContent = TRUE;
|
||||
}
|
||||
@@ -221,7 +311,7 @@ class _Digest
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function send()
|
||||
public function send() : void
|
||||
{
|
||||
if( $this->hasContent )
|
||||
{
|
||||
@@ -230,11 +320,11 @@ class _Digest
|
||||
$htmlContent = str_replace( "___digest___", $this->output['html'], $this->mail->compileContent( 'html', $this->member ) );
|
||||
$plaintextContent = str_replace( "___digest___", $this->output['plain'], $this->mail->compileContent( 'plaintext', $this->member ) );
|
||||
|
||||
\IPS\Email::buildFromContent( $subject, $htmlContent, $plaintextContent, \IPS\Email::TYPE_LIST, \IPS\Email::WRAPPER_NONE, $this->frequency . '_digest' )->send( $this->member );
|
||||
Email::buildFromContent( $subject, $htmlContent, $plaintextContent, Email::TYPE_LIST, Email::WRAPPER_NONE, $this->frequency . '_digest' )->send( $this->member );
|
||||
}
|
||||
|
||||
/* After sending digest update core_follows to set notify_sent (don't forget where clause for frequency) */
|
||||
\IPS\Db::i()->update( 'core_follow', array( 'follow_notify_sent' => time() ), array( 'follow_member_id=? AND follow_notify_freq=?', $this->member->member_id, $this->frequency ) );
|
||||
Db::i()->update( 'core_follow', array( 'follow_notify_sent' => time() ), array( 'follow_member_id=? AND follow_notify_freq=?', $this->member->member_id, $this->frequency ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,12 +334,12 @@ class _Digest
|
||||
* @param int $numberToSend The number of digests to send for this batch
|
||||
* @return bool
|
||||
*/
|
||||
public static function sendDigestBatch( $frequency='daily', $numberToSend=50 )
|
||||
public static function sendDigestBatch( string $frequency='daily', int $numberToSend=50 ) : bool
|
||||
{
|
||||
/* Grab some members to send digests to. */
|
||||
$members = iterator_to_array( \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, $numberToSend ), NULL, NULL, \IPS\Db::SELECT_DISTINCT ) );
|
||||
$members = iterator_to_array( 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, $numberToSend ), NULL, NULL, Db::SELECT_DISTINCT ) );
|
||||
|
||||
if( !\count( $members ) )
|
||||
if( !count( $members ) )
|
||||
{
|
||||
/* Nothing to send */
|
||||
return FALSE;
|
||||
@@ -262,8 +352,7 @@ class _Digest
|
||||
}
|
||||
|
||||
/* Fetch the member's follows so we can build their digest */
|
||||
/* @note SELECT_FROM_WRITE_SERVER: Avoid issue where the follow_notify_sent time below is updated on the writer, but not on the readers on the next task run */
|
||||
$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, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800 ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER );
|
||||
$follows = Db::i()->select( '*', 'core_follow', array( 'follow_notify_do=1 AND follow_notify_freq=? AND follow_notify_sent < ? AND ' . Db::i()->in( 'follow_member_id', $memberIDs ), $frequency, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800 ), NULL, NULL, NULL, NULL, Db::SELECT_FROM_WRITE_SERVER );
|
||||
|
||||
$groupedFollows = array();
|
||||
foreach( $follows as $follow )
|
||||
@@ -273,11 +362,11 @@ class _Digest
|
||||
|
||||
foreach( $groupedFollows as $id => $data )
|
||||
{
|
||||
$member = \IPS\Member::load( $id );
|
||||
$member = Member::load( $id );
|
||||
if( !$member->email )
|
||||
{
|
||||
/* Update notification sent time, so the batch doesn't get stuck in a loop */
|
||||
\IPS\Db::i()->update( 'core_follow', array( 'follow_notify_sent' => time() ), array( 'follow_member_id=? AND follow_notify_freq=?', $id, $frequency ) );
|
||||
Db::i()->update( 'core_follow', array( 'follow_notify_sent' => time() ), array( 'follow_member_id=? AND follow_notify_freq=?', $id, $frequency ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user