Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+519 -125
View File
@@ -11,7 +11,7 @@
namespace IPS;
/* 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;
@@ -36,149 +36,353 @@ class _Notification
{
if ( static::$defaultConfiguration === NULL )
{
static::$defaultConfiguration = iterator_to_array( \IPS\Db::i()->select( '*', 'core_notification_defaults' )->setKeyField('notification_key') );
/* Get data from the actual extensions */
$extensionDefaults = array();
foreach( \IPS\Application::allExtensions( 'core', 'Notifications' ) as $group => $class )
{
$configuration = $class->getConfiguration( NULL );
if ( !empty( $configuration ) )
if ( method_exists( $class, 'configurationOptions' ) )
{
foreach ( $configuration as $key => $data )
foreach ( $class->configurationOptions( NULL ) as $key => $details )
{
if ( !isset( static::$defaultConfiguration[ $key ] ) )
if ( $details['type'] === 'standard' )
{
/* Row isn't in DB, add it */
\IPS\Db::i()->insert( 'core_notification_defaults', array(
'notification_key' => $key,
'default' => implode( ',', $data['default'] ),
'disabled' => implode( ',', $data['disabled'] )
) );
$extensionDefaults[ $key ] = array(
'default' => $details['default'],
'disabled' => $details['disabled']
);
static::$defaultConfiguration[ $key ] = array_merge( $data, array( 'editable' => TRUE ) );
foreach ( $details['notificationTypes'] as $type )
{
$extensionDefaults[ $type ] = array(
'default' => $details['default'],
'disabled' => $details['disabled']
);
}
}
else
{
static::$defaultConfiguration[ $key ]['default'] = array_filter( explode( ',', static::$defaultConfiguration[ $key ]['default'] ) );
static::$defaultConfiguration[ $key ]['disabled'] = array_filter( explode( ',', static::$defaultConfiguration[ $key ]['disabled'] ) );
}
}
}
}
}
return static::$defaultConfiguration;
}
/**
* Build Matrix
*
* @param \IPS\Member $member The member
* @return \IPS\Helpers\Form\Matrix
*/
public static function buildMatrix( \IPS\Member $member )
{
$matrix = new \IPS\Helpers\Form\Matrix();
$matrix->manageable = FALSE;
$matrix->langPrefix = FALSE;
$matrix->columns = array(
'label' => function( $key, $value, $data )
{
if ( mb_substr( $key, 0, -7 ) === 'new_likes' )
{
if ( \IPS\Content\Reaction::isLikeMode() )
{
$return = 'notifications__new_likes_like';
}
else
{
$return = 'notifications__new_likes_rep';
}
}
else
{
$return = 'notifications__' . mb_substr( $key, 0, -7 );
$configuration = $class->getConfiguration( NULL );
if ( !empty( $configuration ) )
{
foreach ( $configuration as $key => $data )
{
$extensionDefaults[ $key ] = $data;
}
}
}
return \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->notificationLabel( $return, $data );
},
'member_notifications_inline' => function( $key, $value, $data )
}
/* Combine that with what the admin has set */
static::$defaultConfiguration = iterator_to_array( \IPS\Db::i()->select( '*', 'core_notification_defaults' )->setKeyField('notification_key') );
foreach ( $extensionDefaults as $key => $data )
{
return new \IPS\Helpers\Form\YesNo( $key, in_array( 'inline', ( is_array( $data['selected'] ) ) ? $data['selected'] : array() ), FALSE, array( 'disabled' => ( !$data['editable'] OR in_array( 'inline', $data['disabled'] ) ), 'tooltip' => ( !$data['editable'] OR in_array( 'inline', $data['disabled'] ) ) ? \IPS\Member::loggedIn()->language()->addToStack('admin_notification_disabled') : NULL ) );
},
'member_notifications_email' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\YesNo( $key, in_array( 'email', ( is_array( $data['selected'] ) ) ? $data['selected'] : array() ), FALSE, array( 'disabled' => ( !$data['editable'] OR in_array( 'email', $data['disabled'] ) ), 'tooltip' => ( !$data['editable'] OR in_array( 'email', $data['disabled'] ) ) ? \IPS\Member::loggedIn()->language()->addToStack('admin_notification_disabled') : NULL ) );
},
);
/* Add rows */
$defaultConfiguration = static::defaultConfiguration();
$memberConfiguration = $member->notificationsConfiguration();
foreach( \IPS\Application::allExtensions( 'core', 'Notifications' ) as $group => $class )
{
$configuration = $class->getConfiguration( $member );
if ( !empty( $configuration ) )
{
$lang = "notifications__{$group}";
$header = \IPS\Member::loggedIn()->language()->addToStack( $lang );
$matrix->rows[] = $header;
foreach ( $configuration as $key => $data )
if ( !isset( static::$defaultConfiguration[ $key ] ) )
{
$matrix->rows[ $key ] = array( 'selected' => ( !empty( $memberConfiguration[ $key ] ) ) ? $memberConfiguration[ $key ] : NULL, 'disabled' => $defaultConfiguration[ $key ]['disabled'], 'icon' => isset( $data['icon'] ) ? $data['icon'] : NULL, 'editable' => $defaultConfiguration[ $key ]['editable'] );
/* Row isn't in DB, add it */
\IPS\Db::i()->insert( 'core_notification_defaults', array(
'notification_key' => $key,
'default' => implode( ',', $data['default'] ),
'disabled' => implode( ',', $data['disabled'] )
) );
static::$defaultConfiguration[ $key ] = array_merge( $data, array( 'editable' => TRUE ) );
}
else
{
static::$defaultConfiguration[ $key ]['default'] = array_filter( explode( ',', static::$defaultConfiguration[ $key ]['default'] ) );
static::$defaultConfiguration[ $key ]['disabled'] = array_filter( array_merge( $extensionDefaults[ $key ]['disabled'], explode( ',', static::$defaultConfiguration[ $key ]['disabled'] ) ) );
}
}
}
return $matrix;
return static::$defaultConfiguration;
}
/**
* Save Matrix
* Get available options for an extension
*
* @param \IPS\Member $member The member
* @param array $values Values from matrix
* @param \IPS\Member|NULL $member The member or NULL to get all
* @param object $extension The noticiations extension
* @return void
*/
public static function saveMatrix( \IPS\Member $member, array $values )
public static function availableOptions( $member, $extension )
{
/* Remove the current preferences */
\IPS\Db::i()->delete( 'core_notification_preferences', array( 'member_id=?', $member->member_id ) );
/* Get the default configuration so we know what is forced enabled by the admin */
$defaults = static::defaultConfiguration();
/* Now loop over the notifications and set preferences */
$insert = array();
foreach ( $values['notifications'] as $k => $v )
/* First of all, get the settings from the extension, maintaining backwards compatibility with older extensions */
if ( method_exists( $extension, 'configurationOptions' ) )
{
$pref = array();
if ( $v['member_notifications_inline'] )
$settings = $extension::configurationOptions( $member );
}
else
{
$configuration = $extension->getConfiguration( $member );
$settings = array();
foreach ( $configuration as $key => $details )
{
$pref[] = 'inline';
$settings[ $key ] = array(
'type' => 'standard',
'notificationTypes' => array( $key ),
'title' => "notifications__{$key}",
'showTitle' => TRUE,
'description' => NULL,
);
}
else if( !$defaults[ $k ]['editable'] AND in_array( 'inline', $defaults[ $k ]['default'] ) )
}
/* Now loop through each of those and get the value for each */
if ( $member )
{
$defaultConfiguration = \IPS\Notification::defaultConfiguration();
$memberConfiguration = $member->notificationsConfiguration();
$finalSettings = array();
foreach ( $settings as $key => $details )
{
$pref[] = 'inline';
}
if ( $v['member_notifications_email'] )
{
$pref[] = 'email';
}
else if( !$defaults[ $k ]['editable'] AND in_array( 'email', $defaults[ $k ]['default'] ) )
{
$pref[] = 'email';
if ( $details['type'] === 'standard' )
{
$options = array();
$availableTypes = array('inline');
if ( \IPS\Settings::i()->mobile_app_id and $member->members_bitoptions['mobile_notifications'] )
{
$availableTypes[] = 'push';
}
$availableTypes[] = 'email';
foreach ( $availableTypes as $type )
{
if ( !\in_array( $type, $defaultConfiguration[ $key ]['disabled'] ) )//if ( !\in_array( $type, $defaultConfiguration[ $key ]['disabled'] ) and ( $type !== 'push' or !\in_array( 'inline', $defaultConfiguration[ $key ]['disabled'] ) ) )
{
$enabled = array();
$haveMemberPreferences = FALSE;
if ( $defaultConfiguration[ $key ]['editable'] )
{
foreach ( $details['notificationTypes'] as $notificationType )
{
if ( isset( $memberConfiguration[ $notificationType ] ) )
{
$haveMemberPreferences = TRUE;
$enabled += $memberConfiguration[ $notificationType ];
}
}
}
if ( !$haveMemberPreferences )
{
$enabled = $defaultConfiguration[ $key ]['default'];
}
$options[ $type ] = array(
'title' => "member_notifications_{$type}",
'value' => \in_array( $type, $enabled ),
'editable' => $defaultConfiguration[ $key ]['editable']
);
}
}
if ( \count( $options ) or ( isset( $details['extra'] ) and \count( $details['extra'] ) ) )
{
$details['options'] = $options;
$finalSettings[ $key ] = $details;
}
}
else
{
$finalSettings[ $key ] = $details;
}
}
$insert[] = array(
'member_id' => $member->member_id,
'notification_key' => $k,
'preference' => implode( ',', $pref )
);
/* And return */
return $finalSettings;
}
else
{
return $settings;
}
\IPS\Db::i()->insert( 'core_notification_preferences', $insert );
}
/**
* Get the notification categories to show to a member
*
* @param \IPS\Member $member The member
* @param array $extensions The notification extensions (passed so we don't have to load it more than once)
* @return array
*/
public static function membersOptionCategories( \IPS\Member $member, $extensions )
{
$categories = array();
foreach( $extensions as $group => $extension )
{
$options = static::availableOptions( $member, $extension );
if ( !empty( $options ) )
{
if ( !isset( $categories[ $group ] ) )
{
$categories[ $group ] = array();
}
foreach ( $options as $key => $details )
{
if ( isset( $details['extra'] ) )
{
foreach ( $details['extra'] as $extraKey => $extraDetails )
{
if ( $extraDetails['value'] )
{
$categories[ $group ][ $extraKey ] = array( 'title' => \IPS\Member::loggedIn()->language()->get( $extraDetails['title'] ), 'icon' => $extraDetails['icon'], 'description' => isset( $extraDetails['description'] ) ? $extraDetails['description'] : NULL );
}
}
}
if ( $details['type'] === 'standard' )
{
foreach ( $details['options'] as $type => $typeDetails )
{
if ( $typeDetails['value'] )
{
switch ( $type )
{
case 'inline':
if ( isset( $details['options']['push'] ) and $details['options']['push']['value'] )
{
continue 2;
}
$icon = 'bell-o';
break;
case 'push':
$icon = 'mobile';
break;
case 'email':
$icon = 'envelope-o';
break;
}
$categories[ $group ][ $type ] = array( 'title' => \IPS\Member::loggedIn()->language()->get( 'member_notifications_' . $type ), 'icon' => $icon );
}
}
}
}
}
}
return $categories;
}
/**
* Get the form for editing a member's notification preferences for a given notification extension
*
* @param \IPS\Member $member The member
* @param object $extension The notification extension to edit preferences for
* @return \IPS\Helpers\Form|NULL|TRUE
*/
public static function membersTypeForm( \IPS\Member $member, $extension )
{
$form = NULL;
$formIsEditable = FALSE;
if ( $options = \IPS\Notification::availableOptions( $member, $extension ) )
{
$form = new \IPS\Helpers\Form;
foreach ( $options as $key => $details )
{
if ( $details['type'] === 'separator' and \IPS\Dispatcher::i()->controllerLocation === 'front' )
{
$form->addSeparator();
}
elseif ( $details['type'] === 'header' )
{
$form->addHeader( $details['header'] );
}
elseif ( $details['type'] === 'custom' and ( !isset( $details['adminOnly'] ) ) )
{
$form->add( $details['field'] );
$formIsEditable = TRUE;
}
elseif ( $details['type'] === 'standard' )
{
if ( !$formIsEditable )
{
if ( isset( $details['extra'] ) and \count( $details['extra'] ) )
{
$formIsEditable = TRUE;
}
else
{
foreach ( $details['options'] as $option )
{
if ( $option['editable'] )
{
$formIsEditable = TRUE;
break;
}
}
}
}
$form->add( new \IPS\Helpers\Form\Custom( "notifications_{$key}", NULL, TRUE, array(
'rowHtml' => function( $field ) use ( $details, $options ) {
return \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->notificationsSettingsRow( $field, $details );
}
) ) );
}
}
if ( $values = $form->values() )
{
foreach ( $options as $key => $details )
{
if ( $details['type'] === 'standard' )
{
$value = array();
if ( isset( $values["notifications_{$key}"] ) )
{
foreach ( $values["notifications_{$key}"] as $k => $v )
{
if ( $v === 'push' )
{
$value['inline'] = 'inline';
$value['push'] = 'push';
}
elseif ( \in_array( $k, array( 'inline', 'push', 'email' ) ) and $v )
{
$value[ $k ] = $k;
}
}
}
foreach ( $details['notificationTypes'] as $notificationKey )
{
\IPS\Db::i()->insert( 'core_notification_preferences', array(
'member_id' => $member->member_id,
'notification_key' => $notificationKey,
'preference' => implode( ',', $value )
), TRUE );
}
}
elseif ( $details['type'] === 'custom' and ( !isset( $details['adminOnly'] ) or !$details['adminOnly'] ) )
{
$extension::saveExtra( $member, $key, $values[ $key ] );
}
if ( isset( $details['extra'] ) )
{
foreach ( $details['extra'] as $extraKey => $extraDetails )
{
$extension::saveExtra( $member, $extraKey, array_key_exists( $extraKey, $values["notifications_{$key}"] ?? [] ) );
}
}
}
$member->save();
return TRUE;
}
}
if ( $form and !$formIsEditable )
{
$form->actionButtons = array();
}
return $form;
}
/**
* @brief Application
*/
@@ -265,6 +469,60 @@ class _Notification
$emails = array();
$emailRecipients = array();
$thingsBeingFollowed = array();
$pushNotifications = array();
/* First, loop over the members so we can load their notification preferences en-masse */
$membersForNotifications = array();
foreach ( $this->recipients as $member )
{
/* Let's not send notifications to deleted members, banned members or spammers */
if ( $member === NULL or !$member->member_id or $member->isBanned() or $member->members_bitoptions['bw_is_spammer'] )
{
continue;
}
$membersForNotifications[ $member->member_id ] = $member;
}
if( \count( $membersForNotifications ) )
{
/* Fill in any that may not have customized their preferences */
foreach( $membersForNotifications as $member )
{
if( $member->notificationsConfiguration === NULL )
{
$member->notificationsConfiguration = array();
}
}
/* Get all preferences at once */
$preferenceSet = array();
foreach (
\IPS\Db::i()->select(
'd.*, p.preference, p.member_id',
array( 'core_notification_defaults', 'd' )
)->join(
array( 'core_notification_preferences', 'p' ),
array( 'd.notification_key=p.notification_key AND p.member_id IN(' . implode( ',', array_keys( $membersForNotifications ) ) . ')' )
)
as $row
) {
if( !\in_array( $row['notification_key'], $preferenceSet ) )
{
foreach( $membersForNotifications as $member )
{
$member->notificationsConfiguration[ $row['notification_key'] ] = explode( ',', $row['default'] );
}
$preferenceSet[] = $row['notification_key'];
}
if ( $row['preference'] !== NULL AND $row['editable'] )
{
$membersForNotifications[ $row['member_id'] ]->notificationsConfiguration[ $row['notification_key'] ] = array_diff( explode( ',', $row['preference'] ), explode( ',', $row['disabled'] ) );
}
}
}
/* Loop recipients */
foreach ( $this->recipients as $member )
@@ -320,7 +578,7 @@ class _Notification
/* Work out how the user wants to receive this notification */
$notificationPreferences = $member->notificationsConfiguration();
$info = $this->recipients->getInfo();
if ( $info['follow_app'] === 'core' and $info['follow_area'] === 'member' )
if ( $info !== NULL and $info['follow_app'] === 'core' and $info['follow_area'] === 'member' )
{
$keyToCheck = 'follower_content';
}
@@ -336,9 +594,9 @@ class _Notification
$keyToCheck = 'unapproved_content';
}
}
/* They want to receive an email (we don't send until the end once we've collated all the emails to send) */
if ( isset( $notificationPreferences[ $keyToCheck ] ) AND in_array( 'email', $notificationPreferences[ $keyToCheck ] ) and ( !isset( $sentTo[ $member->member_id ] ) or !in_array( 'email', $sentTo[ $member->member_id ] ) ) )
if ( isset( $notificationPreferences[ $keyToCheck ] ) AND \in_array( 'email', $notificationPreferences[ $keyToCheck ] ) and ( !isset( $sentTo[ $member->member_id ] ) or !\in_array( 'email', $sentTo[ $member->member_id ] ) ) )
{
$language = $member->language()->id;
@@ -374,26 +632,38 @@ class _Notification
{
$classname = 'IPS\\' . $info['follow_app'] . '\\' . mb_ucfirst( $info['follow_area'] );
$thingsBeingFollowed[ $info['follow_app'] ][ $info['follow_area'] ][ $info['follow_rel_id'] ] = $classname::load( $info['follow_rel_id'] );
/* Set some parameters so the best advertisement possible can be loaded later */
$email->setAdvertisementParameters( $classname, $info['follow_rel_id'] );
}
}
$thingBeingFollowed = $thingsBeingFollowed[ $info['follow_app'] ][ $info['follow_area'] ][ $info['follow_rel_id'] ];
if ( $thingBeingFollowed instanceof \IPS\Member )
{
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow_member', FALSE, array( 'sprintf' => array( $thingBeingFollowed->name ) ) );
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow_member', FALSE, array( 'htmlsprintf' => array( $thingBeingFollowed->name ) ) );
}
elseif ( $thingBeingFollowed instanceof \IPS\Node\Model )
{
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow', FALSE, array( 'sprintf' => array( $member->language()->addToStack( $thingBeingFollowed::$nodeTitle . '_sg' ), $thingBeingFollowed->_title ) ) );
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow', FALSE, array( 'htmlsprintf' => array( $member->language()->addToStack( $thingBeingFollowed::$nodeTitle . '_sg' ), $thingBeingFollowed->getTitleForLanguage( $member->language() ) ) ) );
}
else
{
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow', FALSE, array( 'sprintf' => array( $member->language()->addToStack( $thingBeingFollowed::$title ), $thingBeingFollowed->mapped('title') ) ) );
$unsubscribeBlurb = $member->language()->addToStack( 'unsubscribe_blurb_follow', FALSE, array( 'htmlsprintf' => array( $member->language()->addToStack( $thingBeingFollowed::$title ), $thingBeingFollowed->mapped('title') ) ) );
}
$unfollowLink = (string) \IPS\Http\Url::internal( "app=core&module=system&section=notifications&do=follow&follow_app={$info['follow_app']}&follow_area={$info['follow_area']}&follow_id={$info['follow_rel_id']}", 'front' );
$guestKey = md5( $info['follow_app'] . ';' . $info['follow_area'] . ';' . $info['follow_rel_id'] . ';' . $info['follow_member_id'] . ';' . $info['follow_added'] ) . '-' . md5( $member->email . ';' . $member->ip_address . ';' . $member->joined->getTimestamp() );
$unfollowLink = \IPS\Http\Url::internal( "app=core&module=system&controller=notifications&do=unfollowFromEmail&follow_app={$info['follow_app']}&follow_area={$info['follow_area']}&follow_id={$info['follow_rel_id']}&gkey={$guestKey}", 'front' );
/* If we are tracking email views/clicks, add the tracking info to this URL as the email handler won't be able to */
if( \IPS\Settings::i()->prune_log_emailstats != 0 )
{
$unfollowLink = $unfollowLink->setQueryString( array( 'email' => 1, 'type' => $this->emailKey ) );
}
$unfollowLink = (string) $unfollowLink;
if ( $member->members_bitoptions['email_notifications_once'] and $member->last_activity < $info['follow_notify_sent'] )
if ( $member->members_bitoptions['email_notifications_once'] and max( $member->last_activity, $member->last_visit ) < $info['follow_notify_sent'] )
{
$okToEmail = FALSE;
}
@@ -413,7 +683,8 @@ class _Notification
/* They want to receive an inline notification... (ignore for report center which is treated special and the 'inline' notification
preference actually instead controls whether the bubble should be shown on the report center icon at the top or not) */
if ( $this->key != 'report_center' and isset( $notificationPreferences[ $keyToCheck ] ) and in_array( 'inline', $notificationPreferences[ $keyToCheck ] ) and ( !isset( $sentTo[ $member->member_id ] ) or !in_array( 'inline', $sentTo[ $member->member_id ] ) ) )
$notification = NULL;
if ( $this->key != 'report_center' and isset( $notificationPreferences[ $keyToCheck ] ) and \in_array( 'inline', $notificationPreferences[ $keyToCheck ] ) and ( !isset( $sentTo[ $member->member_id ] ) or !\in_array( 'inline', $sentTo[ $member->member_id ] ) ) )
{
if ( $this->item AND $this->allowMerging )
{
@@ -421,7 +692,7 @@ class _Notification
{
$item = $this->item;
$idColumn = $item::$databaseColumnId;
$notification = \IPS\Notification\Inline::constructFromData( \IPS\Db::i()->select( '*', 'core_notifications', array( 'notification_key=? AND item_class=? AND item_id=? AND member=? AND read_time IS NULL', $this->key, get_class( $this->item ), $item->$idColumn, $member->member_id ) )->first() );
$notification = \IPS\Notification\Inline::constructFromData( \IPS\Db::i()->select( '*', 'core_notifications', array( 'notification_key=? AND item_class=? AND item_id=? AND `member`=? AND read_time IS NULL', $this->key, \get_class( $this->item ), $item->$idColumn, $member->member_id ) )->first() );
$notification->member = $member;
$notification->updated_time = time();
@@ -448,13 +719,13 @@ class _Notification
if ( $param instanceof \IPS\Content )
{
$subIdColumn = $param::$databaseColumnId;
$notification->item_sub_class = get_class( $param );
$notification->item_sub_class = \get_class( $param );
$notification->item_sub_id = $param->$subIdColumn;
/*
* If this is a grouped comment or review, set the sent time to the same time as the comment just in case there is a slight delay
*/
if ( ( $param instanceof \IPS\Content\Comment OR $param instanceof \IPS\Content\Review ) && in_array( $this->key, array( 'new_comment', 'new_review', 'quote', 'new_likes' ) ) )
if ( ( $param instanceof \IPS\Content\Comment OR $param instanceof \IPS\Content\Review ) && \in_array( $this->key, array( 'new_comment', 'new_review', 'quote', 'new_likes' ) ) )
{
if ( $this->key === 'new_likes' and $this->emailParams[1] instanceof \IPS\Member )
{
@@ -482,10 +753,78 @@ class _Notification
$sentTo[ $member->member_id ][] = 'inline';
}
/* They want to receive a mobile app push notification (we don't send until the end once we've collated all the notifications to send) */
if ( \IPS\Settings::i()->mobile_app_id and $member->members_bitoptions['mobile_notifications'] and isset( $notificationPreferences[ $keyToCheck ] ) AND \in_array( 'push', $notificationPreferences[ $keyToCheck ] ) and ( !isset( $sentTo[ $member->member_id ] ) or !\in_array( 'push', $sentTo[ $member->member_id ] ) ) and $licenseKey = \IPS\IPS::licenseKey() and $licenseKey['active'] )
{
$language = $member->language();
if ( !isset( $pushNotifications[ $language->id ] ) )
{
$method = "parse_mobile_{$this->key}";
foreach ( $this->app->extensions( 'core', 'Notifications' ) as $class )
{
if ( method_exists( $class, $method ) or $this->key === 'follower_content' )
{
$data = $class::$method( $language, ...$this->emailParams );
$language->parseOutputForDisplay( $data );
if ( !isset( $data['data'] ) )
{
$data['data'] = array();
}
$data['data']['type'] = $this->app->directory . '/' . $this->key;
if ( isset( $data['data']['url'] ) )
{
$url = \IPS\Http\Url::createFromString( $data['data']['url'] );
if ( $url instanceof \IPS\Http\Url\Internal )
{
foreach ( array( 'app', 'module', 'controller', 'id', 'comment', 'review' ) as $k )
{
if ( isset( $url->hiddenQueryString[ $k ] ) )
{
$data['data'][ $k ] = $url->hiddenQueryString[ $k ];
}
elseif ( isset( $url->queryString[ $k ] ) )
{
$data['data'][ $k ] = $url->queryString[ $k ];
}
}
}
}
if ( isset( $data['data']['author'] ) )
{
$data['data']['image'] = $data['data']['author']->photo;
unset( $data['data']['author'] );
}
$pushNotifications[ $language->id ] = $data;
break;
}
}
}
if ( isset( $pushNotifications[ $language->id ] ) )
{
$pushRecipients[ $member->member_id ] = $pushNotifications[ $language->id ];
$pushRecipients[ $member->member_id ]['member'] = $member->member_id;
$pushRecipients[ $member->member_id ]['unreadCount'] = $member->notification_cnt;
$pushRecipients[ $member->member_id ]['notificationId'] = $notification ? $notification->id : NULL;
}
$sentTo[ $member->member_id ][] = 'push';
}
}
/* Send any emails */
$this->sendEmails( $emails, $emailRecipients );
/* Send any emails and push notifications */
if ( \count( $pushNotifications ) )
{
$this->sendPushNotifications( $pushRecipients );
}
if ( \count( $emails ) )
{
$this->sendEmails( $emails, $emailRecipients );
}
/* And return */
return $sentTo;
@@ -508,4 +847,59 @@ class _Notification
}
}
}
}
/**
* Send push notifications
*
* @param array $pushNotifications Push notifications to send
* @return void
*/
protected function sendPushNotifications( $pushNotifications )
{
$limit = 100;
$offset = 0;
while ( $slice = \array_slice( $pushNotifications, $offset, $limit ) )
{
try
{
/* Send to remoteservices */
$response = \IPS\Http\Url::ips('mobile/notify')->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->post( array( 'data' => $slice ) );
if ( $response->httpResponseCode != 200 )
{
throw new \DomainException( $response );
}
$response = $response->decodeJson();
/* If any users have turned off notifications on all of their devices, we can remove them */
if ( isset( $response['unsubscribe'] ) and $response['unsubscribe'] )
{
\IPS\Db::i()->update( 'core_members', "members_bitoptions2 = members_bitoptions2 & ~" . \IPS\Member::$bitOptions['members_bitoptions']['members_bitoptions2']['mobile_notifications'], \IPS\Db::i()->in( 'member_id', $response['unsubscribe'] ) );
}
/* Log any errors */
if ( isset( $response['errors'] ) and $response['errors'] )
{
\IPS\Log::log( json_encode( $slice, JSON_PRETTY_PRINT ) . "\n\n==============================================================================\n\n" . json_encode( $response, JSON_PRETTY_PRINT ), 'mobile-push-notifications' );
}
}
catch ( \Exception $e )
{
\IPS\Log::log( $e, 'mobile-push-notifications' );
}
$offset += $limit;
}
}
/**
* Convert HTML to plaintext for use in notifications
*
* @param string $html HTML Text
* @return string
* @todo Almost certainly will need to make this more thorough
*/
public static function textForPushNotification( $html )
{
return preg_replace( "/\n\s+/", "\n", trim( html_entity_decode( strip_tags( $html ) ) ) );
}
}