Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

@@ -65,12 +65,84 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notifications');
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Output::i()->title );
\IPS\Output::i()->output = (string) $table;
}
}
/**
* Subscribe a user's device to push notifications
*
* @return void
*/
protected function subscribeToPush()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
if ( isset( \IPS\Request::i()->subscription ) AND $subscription = json_decode( \IPS\Request::i()->subscription, TRUE ) )
{
$device = \IPS\Member\Device::loadOrCreate( \IPS\Member::loggedIn(), FALSE );
/* If a subscription already exists, then just return */
try
{
\IPS\Db::i()->select( '*', 'core_notifications_pwa_keys', array( "`member`=? AND p256dh=? AND auth=?", \IPS\Member::loggedIn()->member_id, $subscription['keys']['p256dh'], $subscription['keys']['auth'] ) )->first();
/* Make sure encoding and device is up to date, though since it's needed for encryption transfer. */
\IPS\Db::i()->update( 'core_notifications_pwa_keys', array(
'encoding' => \IPS\Request::i()->encoding,
'device' => $device->device_key
), array( "`member`=? AND p256dh=? AND auth=?", \IPS\Member::loggedIn()->member_id, $subscription['keys']['p256dh'], $subscription['keys']['auth'] ) );
}
catch( \UnderflowException $e )
{
\IPS\Db::i()->insert( 'core_notifications_pwa_keys', array(
'member' => \IPS\Member::loggedIn()->member_id,
'endpoint' => $subscription['endpoint'],
'p256dh' => $subscription['keys']['p256dh'],
'auth' => $subscription['keys']['auth'],
'encoding' => \IPS\Request::i()->encoding,
'device' => $device->device_key
) );
}
\IPS\Output::i()->json( 'OK' );
}
else
{
\IPS\Output::i()->error( 'invalid_push_subscription', '2C154/H', 403, '' );
}
}
/**
* Verify a subscription with the provided key exists for the logged-in user
*
* @return void
*/
protected function verifySubscription()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
if ( isset( \IPS\Request::i()->key ) )
{
/* See if a subscription already exists */
try
{
\IPS\Db::i()->select( '*', 'core_notifications_pwa_keys', array( "`member`=? AND p256dh=?", \IPS\Member::loggedIn()->member_id, \IPS\Request::i()->key ) )->first();
\IPS\Output::i()->json( 'OK' );
}
catch( \UnderflowException $e )
{
// Just let it return the error below
}
}
\IPS\Output::i()->error( 'invalid_push_subscription', '2C154/I', 403, '' );
}
/**
* Options: Dispatcher
*
@@ -127,8 +199,7 @@ class _notifications extends \IPS\Dispatcher\Controller
if ( $form === TRUE )
{
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Member::loggedIn()->notificationsConfiguration = NULL;
{
$categories = \IPS\Notification::membersOptionCategories( \IPS\Member::loggedIn(), array( $extensionKey => $extension ) );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate('system')->notificationSettingsIndexRowDetails( $extensionKey, $categories[ $extensionKey ] ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
@@ -153,30 +224,6 @@ class _notifications extends \IPS\Dispatcher\Controller
}
}
/**
* Enable/Disable Sounds
*
* @param array $extensions The extensions
* @return void
*/
protected function sounds()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->members_bitoptions['disable_notification_sounds'] = ( \IPS\Request::i()->enable ? false : true );
\IPS\Member::loggedIn()->save();
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'ok' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=notifications&do=options', 'front', 'notifications_options' ) );
}
}
/**
* Stop receiving all notifications to a particular method
*
@@ -219,6 +266,11 @@ class _notifications extends \IPS\Dispatcher\Controller
$extension::disableExtra( \IPS\Member::loggedIn(), \IPS\Request::i()->type );
}
}
if ( \IPS\Request::i()->type === 'push' )
{
\IPS\Member::loggedIn()->clearPwaAuths();
}
if ( \IPS\Request::i()->isAjax() )
{
@@ -239,9 +291,18 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$this->_checkLoggedIn();
try
{
$application = \IPS\Application::load( \IPS\Request::i()->follow_app );
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'error_no_app', '3C154/F', 404, '' );
}
/* Get class */
$class = NULL;
foreach ( \IPS\Application::load( \IPS\Request::i()->follow_app )->extensions( 'core', 'ContentRouter' ) as $ext )
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $ext )
{
foreach ( $ext->classes as $classname )
{
@@ -309,7 +370,7 @@ class _notifications extends \IPS\Dispatcher\Controller
{
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
/* Set navigation */
@@ -325,7 +386,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
elseif ( $class == 'IPS\Member\Club' )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
\IPS\Output::i()->breadcrumb = array(
array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') ),
@@ -333,8 +394,13 @@ class _notifications extends \IPS\Dispatcher\Controller
);
}
elseif ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
{
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) )
{
throw new \OutOfRangeException;
}
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->mapped('title') ) ) );
/* Set navigation */
@@ -355,7 +421,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('follow_thing', FALSE, array( 'sprintf' => array( $thing->name ) ) );
@@ -535,10 +601,28 @@ class _notifications extends \IPS\Dispatcher\Controller
/* Send notification if following member */
if( $class == "IPS\Member" )
{
/* Give points */
$receiver = \IPS\Member::load( \IPS\Request::i()->follow_rel_id );
$receiver->achievementAction( 'core', 'FollowMember', [
'giver' => \IPS\Member::loggedIn()
] );
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'member_follow', \IPS\Member::loggedIn(), array( \IPS\Member::loggedIn() ) );
$notification->recipients->attach( $thing );
$notification->send();
}
else if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
\IPS\Member::loggedIn()->achievementAction( 'core', 'FollowNode', $class::load( \IPS\Request::i()->follow_id ) );
}
else if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$item = $class::load( \IPS\Request::i()->follow_id );
\IPS\Member::loggedIn()->achievementAction( 'core', 'FollowContentItem', [
'item' => $item,
'author' => $item->author()
] );
}
/* Boink */
if ( \IPS\Request::i()->isAjax() )
@@ -597,7 +681,7 @@ class _notifications extends \IPS\Dispatcher\Controller
try
{
$thing = $class::loadAndCheckPerms( $follow['follow_rel_id'] );
$thing = $class::loadAndCheckPerms( (int) $follow['follow_rel_id'] );
foreach ( $thing->nodes() as $node )
{
@@ -651,7 +735,7 @@ class _notifications extends \IPS\Dispatcher\Controller
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
}
if ( !class_exists( $class ) )
if ( !class_exists( $class ) or !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
\IPS\Output::i()->error( 'node_error', '2C154/5', 404, '' );
}
@@ -665,7 +749,7 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $containerClass::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $classname::containerFollowers( $thing, $classname::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $classname::containerFollowerCount( $thing );
$anonymous = $classname::containerFollowerCount( $thing, $classname::FOLLOW_ANONYMOUS );
@@ -673,7 +757,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else if ( $class == "IPS\Member\Club" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -681,7 +765,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else if ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -689,7 +773,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -735,7 +819,7 @@ class _notifications extends \IPS\Dispatcher\Controller
* @return void
*/
protected function unfollowFromEmail()
{
{
/* Logged in? */
if ( \IPS\Member::loggedIn()->member_id )
{
@@ -764,6 +848,11 @@ class _notifications extends \IPS\Dispatcher\Controller
throw new \Exception;
}
if( !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
throw new \Exception;
}
/* Get class */
if( \IPS\Request::i()->follow_app == 'core' and \IPS\Request::i()->follow_area == 'member' )
{
@@ -776,6 +865,11 @@ class _notifications extends \IPS\Dispatcher\Controller
else
{
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) )
{
throw new \Exception;
}
}
if ( !class_exists( $class ) )
{
@@ -789,22 +883,22 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::load( \IPS\Request::i()->follow_id );
$thing = $containerClass::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->_title;
}
else if ( $class == "IPS\Member\Club" )
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->_title;
}
else if ( $class != "IPS\Member" )
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->mapped('title');
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->name;
}
@@ -890,7 +984,8 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
}
if ( !class_exists( $class ) )
if ( !class_exists( $class ) or !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
\IPS\Output::i()->error( 'node_error', '2C154/5', 404, '' );
}
@@ -903,17 +998,22 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $containerClass::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$count = $classname::containerFollowerCount( $thing );
}
else if ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$count = $thing->followersCount();
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) AND $class != "IPS\Member" )
{
\IPS\Output::i()->error( 'node_error', '2C154/J', 404, '' );
}
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$count = $thing->followersCount();
}
}
@@ -983,4 +1083,39 @@ class _notifications extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Request::i()->referrer() ?: \IPS\Http\Url::internal( '' ), 'followers_removed' );
}
/**
* Retrieve notification data and return it to the service worker
*
* @return void
*/
protected function fetchNotification()
{
/* Got the ID? */
if( !\IPS\Request::i()->id )
{
\IPS\Output::i()->json( array( 'error' => 'missing_id' ), 404 );
}
/* Got the notification? */
try
{
$notification = \IPS\Db::i()->select( '*', 'core_notifications_pwa_queue', array( 'id=?', \IPS\Request::i()->id ) )->first();
}
catch( \UnderflowException $e )
{
\IPS\Output::i()->json( array( 'error' => 'not_found' ), 404 );
}
/* Is this our notification? */
$data = json_decode( $notification['notification_data'], true );
if( !isset( $data['member'] ) OR $data['member'] != \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->json( array( 'error' => 'no_permission' ), 403 );
}
/* Send the data */
\IPS\Output::i()->json( $data );
}
}