Version 5.0.13
This commit is contained in:
1 parent
b89858b2ff
commit
5efe744c5f
1407 files changed
+69690
-30177
No files matched your search
@@ -13,9 +13,12 @@ namespace IPS\core\modules\front\system;
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use Exception;
|
||||
use IPS\Api\Webhook;
|
||||
use IPS\Application;
|
||||
use IPS\Content;
|
||||
use IPS\Content\Item;
|
||||
use IPS\core\DataLayer;
|
||||
use IPS\core\Followed\Follow;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Events\Event;
|
||||
@@ -31,14 +34,17 @@ use IPS\Node\Model;
|
||||
use IPS\Notification;
|
||||
use IPS\Notification\Table;
|
||||
use IPS\Output;
|
||||
use IPS\Platform\Bridge;
|
||||
use IPS\Request;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function md5;
|
||||
use const IPS\REBUILD_QUICK;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
@@ -132,6 +138,37 @@ class notifications extends Controller
|
||||
'encoding' => Request::i()->encoding,
|
||||
'device' => $device->device_key
|
||||
) );
|
||||
|
||||
/* The expectation is to receive some push notifications, so let's add them to follower_content, new_content and new_comment and they can be removed manually if the user doesn't want them */
|
||||
$notificationTypes = array( 'follower_content', 'new_content', 'new_comment' );
|
||||
|
||||
foreach ( $notificationTypes as $notificationKey )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* See if we have a row in preferences already */
|
||||
$currentRow = Db::i()->select( '*', 'core_notification_preferences', array( 'member_id=? AND notification_key=?', Member::loggedIn()->member_id, $notificationKey ) )->first();
|
||||
|
||||
/* Does this row have a preference of push? */
|
||||
$prefs = explode( ',', $currentRow['preference'] );
|
||||
|
||||
if ( !in_array( 'push', $prefs ) )
|
||||
{
|
||||
$prefs[] = 'push';
|
||||
}
|
||||
Db::i()->update( 'core_notification_preferences', [
|
||||
'preference' => implode( ',', $prefs ),
|
||||
], array( 'member_id=? AND notification_key=?', Member::loggedIn()->member_id, $notificationKey ) );
|
||||
}
|
||||
catch( UnderflowException $e )
|
||||
{
|
||||
Db::i()->insert( 'core_notification_preferences', [
|
||||
'member_id' => Member::loggedIn()->member_id,
|
||||
'notification_key' => $notificationKey,
|
||||
'preference' => implode( ',', [ 'inline', 'push' ] )
|
||||
], true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Output::i()->json( 'OK' );
|
||||
@@ -239,13 +276,14 @@ class notifications extends Controller
|
||||
$form->class = 'ipsForm--vertical ipsForm--notification-options';
|
||||
}
|
||||
|
||||
if ( Request::i()->isAjax() )
|
||||
if ( Request::i()->isAjax() and ! isset( Request::i()->fromFollowButton ) )
|
||||
{
|
||||
$form->actionButtons = array();
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate('system')->notificationSettingsType( Member::loggedIn()->language()->addToStack("notifications__{$extensionKey}"), $form, TRUE ), 200, 'text/html', Output::i()->httpHeaders );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If it's not ajax, or come via the follow button, then we want to show the form with the save button */
|
||||
Output::i()->output = Theme::i()->getTemplate('system')->notificationSettingsType( Member::loggedIn()->language()->addToStack("notifications__{$extensionKey}"), $form, FALSE );
|
||||
}
|
||||
}
|
||||
@@ -327,34 +365,7 @@ class notifications extends Controller
|
||||
}
|
||||
|
||||
/* Get class */
|
||||
$class = NULL;
|
||||
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $ext )
|
||||
{
|
||||
foreach ( $ext->classes as $classname )
|
||||
{
|
||||
if ( $classname == 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area ) )
|
||||
{
|
||||
$class = $classname;
|
||||
break;
|
||||
}
|
||||
if ( isset( $classname::$containerNodeClass ) and $classname::$containerNodeClass == 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area ) )
|
||||
{
|
||||
$class = $classname::$containerNodeClass;
|
||||
break;
|
||||
}
|
||||
if( isset( $classname::$containerFollowClasses ) )
|
||||
{
|
||||
foreach( $classname::$containerFollowClasses as $followClass )
|
||||
{
|
||||
if( $followClass == 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area ) )
|
||||
{
|
||||
$class = $followClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$class = Follow::getClassToFollow( Request::i()->follow_app, Request::i()->follow_area );
|
||||
|
||||
if( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'member' )
|
||||
{
|
||||
@@ -376,18 +387,6 @@ class notifications extends Controller
|
||||
{
|
||||
Output::i()->error( 'cant_follow_member', '3C154/8', 403, '' );
|
||||
}
|
||||
|
||||
$class = 'IPS\\Member';
|
||||
}
|
||||
|
||||
if( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'club' )
|
||||
{
|
||||
$class = 'IPS\Member\Club';
|
||||
}
|
||||
|
||||
if( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'tag' )
|
||||
{
|
||||
$class = 'IPS\Content\Tag';
|
||||
}
|
||||
|
||||
if ( !$class )
|
||||
@@ -401,7 +400,7 @@ class notifications extends Controller
|
||||
{
|
||||
if ( in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
/* @var Model $thing */
|
||||
/* @var Model $class */
|
||||
$thing = $class::loadAndCheckPerms( (int) Request::i()->follow_id );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
|
||||
|
||||
@@ -418,6 +417,7 @@ class notifications extends Controller
|
||||
}
|
||||
elseif ( $class == 'IPS\Member\Club' )
|
||||
{
|
||||
/* @var Club $class */
|
||||
$thing = $class::loadAndCheckPerms( (int) Request::i()->follow_id );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
|
||||
Output::i()->breadcrumb = array(
|
||||
@@ -432,7 +432,7 @@ class notifications extends Controller
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
|
||||
/* @var Item $thing */
|
||||
/* @var Item $class */
|
||||
$thing = $class::loadAndCheckPerms( (int) Request::i()->follow_id );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->mapped('title') ) ) );
|
||||
|
||||
@@ -553,7 +553,7 @@ class notifications extends Controller
|
||||
{
|
||||
foreach ( $options as $k => $v )
|
||||
{
|
||||
$form->hiddenValues[ $k ] = $v;
|
||||
$form->hiddenValues[ 'follow_type' ] = $k;
|
||||
if ( empty( $notificationConfiguration ) )
|
||||
{
|
||||
$type = $type == 'follower_content' ? 'core_Content' : $type;
|
||||
@@ -581,91 +581,26 @@ class notifications extends Controller
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Insert */
|
||||
$save = array(
|
||||
'follow_id' => md5( Request::i()->follow_app . ';' . Request::i()->follow_area . ';' . Request::i()->follow_id . ';' . Member::loggedIn()->member_id ),
|
||||
'follow_app' => Request::i()->follow_app,
|
||||
'follow_area' => Request::i()->follow_area,
|
||||
'follow_rel_id' => Request::i()->follow_id,
|
||||
'follow_member_id' => Member::loggedIn()->member_id,
|
||||
'follow_is_anon' => !$values['follow_public'],
|
||||
'follow_added' => time(),
|
||||
'follow_notify_do' => ( isset( $values['follow_type'] ) AND $values['follow_type'] == 'none' ) ? 0 : 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => ( $class == "IPS\Member" ) ? 'immediate' : $values['follow_type'],
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1,
|
||||
);
|
||||
if ( $current )
|
||||
{
|
||||
Db::i()->update( 'core_follow', $save, array( 'follow_id=?', $current['follow_id'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Db::i()->insert( 'core_follow', $save );
|
||||
}
|
||||
$class = Follow::getClassToFollow( Request::i()->follow_app, Request::i()->follow_area );
|
||||
try
|
||||
{
|
||||
$useDataLayer = Member::loggedIn()->member_id and DataLayer::enabled( 'analytics_full' );
|
||||
$alreadyFollowing = $useDataLayer ? Db::i()->select( 'count(*)', 'core_follow', [
|
||||
'follow_app=? AND follow_area=? AND follow_rel_id=? AND follow_member_id=?', Request::i()->follow_app, Request::i()->follow_area, Request::i()->follow_id, Member::loggedIn()->member_id
|
||||
] )->first() : false;
|
||||
$thing = $class::load( Request::i()->follow_id );
|
||||
$thing->follow( $values['follow_type'], $values['follow_public'] );
|
||||
|
||||
/* Fire event */
|
||||
Event::fire( 'onFollow', Member::loggedIn(), [ $thing, !$values['follow_public'] ] );
|
||||
|
||||
/* Remove cached */
|
||||
Db::i()->delete( 'core_follow_count_cache', array( 'id=? AND class=?', Request::i()->follow_id, 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area ) ) );
|
||||
|
||||
/* Also follow all nodes if following club */
|
||||
if( $class == "IPS\Member\Club" )
|
||||
{
|
||||
foreach ( $thing->nodes() as $node )
|
||||
if ( $useDataLayer and !$alreadyFollowing )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
$save = array(
|
||||
'follow_id' => md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . Member::loggedIn()->member_id ),
|
||||
'follow_app' => $followApp,
|
||||
'follow_area' => $followArea,
|
||||
'follow_rel_id' => $node['node_id'],
|
||||
'follow_member_id' => Member::loggedIn()->member_id,
|
||||
'follow_is_anon' => !$values['follow_public'],
|
||||
'follow_added' => time(),
|
||||
'follow_notify_do' => ( isset( $values['follow_type'] ) AND $values['follow_type'] == 'none' ) ? 0 : 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => $values['follow_type'],
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1,
|
||||
);
|
||||
Db::i()->insert( 'core_follow', $save, TRUE );
|
||||
DataLayer::i()->addEvent( 'follow', [
|
||||
'follow_app' => Request::i()->follow_app,
|
||||
'follow_area' => Request::i()->follow_area,
|
||||
'follow_id' => Request::i()->follow_id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send notification if following member */
|
||||
if( $class == "IPS\Member" )
|
||||
{
|
||||
if( $values['follow_public'] )
|
||||
{
|
||||
/* Give points */
|
||||
$receiver = Member::load( Request::i()->follow_id );
|
||||
$receiver->achievementAction( 'core', 'FollowMember', [
|
||||
'giver' => Member::loggedIn()
|
||||
] );
|
||||
|
||||
$notification = new Notification( Application::load( 'core' ), 'member_follow', Member::loggedIn(), array( Member::loggedIn() ) );
|
||||
$notification->recipients->attach( $thing );
|
||||
$notification->send();
|
||||
}
|
||||
}
|
||||
else if ( in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
Member::loggedIn()->achievementAction( 'core', 'FollowNode', $class::load( Request::i()->follow_id ) );
|
||||
}
|
||||
else if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
|
||||
{
|
||||
$item = $class::load( Request::i()->follow_id );
|
||||
Member::loggedIn()->achievementAction( 'core', 'FollowContentItem', [
|
||||
'item' => $item,
|
||||
'author' => $item->author()
|
||||
] );
|
||||
}
|
||||
}
|
||||
catch( OutOfRangeException ){}
|
||||
|
||||
/* Boink */
|
||||
if ( Request::i()->isAjax() )
|
||||
@@ -704,63 +639,33 @@ class notifications extends Controller
|
||||
|
||||
try
|
||||
{
|
||||
$follow = Db::i()->select( '*', 'core_follow', array( 'follow_id=? AND follow_member_id=?', Request::i()->id, Member::loggedIn()->member_id ) )->first();
|
||||
$follow = Follow::load( Request::i()->id );
|
||||
if( $follow->member_id != Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
}
|
||||
catch ( UnderflowException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
Output::i()->error( 'cant_find_unfollow', '2C154/D', 404, '' );
|
||||
}
|
||||
|
||||
/* Fire event */
|
||||
if( $follow['follow_app'] == 'core' AND $follow['follow_area'] == 'member' )
|
||||
{
|
||||
$class = 'IPS\\Member';
|
||||
}
|
||||
elseif( $follow['follow_app'] == 'core' AND $follow['follow_area'] == 'club' )
|
||||
{
|
||||
$class = 'IPS\\Member\\Club';
|
||||
}
|
||||
elseif( $follow['follow_app'] == 'core' and $follow['follow_area'] == 'tag' )
|
||||
{
|
||||
$class = 'IPS\Content\Tag';
|
||||
}
|
||||
else
|
||||
{
|
||||
$class = 'IPS\\' . $follow['follow_app'] . '\\' . IPS::mb_ucfirst( $follow['follow_area'] );
|
||||
}
|
||||
try
|
||||
{
|
||||
$thing = $class::load( $follow['follow_rel_id'] );
|
||||
Event::fire( 'onUnfollow', Member::loggedIn(), [ $thing ] );
|
||||
}
|
||||
catch( OutOfRangeException ){}
|
||||
|
||||
Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', Request::i()->id, Member::loggedIn()->member_id ) );
|
||||
Db::i()->delete( 'core_follow_count_cache', array( 'id=? AND class=?', $follow['follow_rel_id'], 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area ) ) );
|
||||
|
||||
/* If we are unfollowing a club, unfollow all of its nodes */
|
||||
if( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'club' )
|
||||
{
|
||||
$class = 'IPS\\Member\Club';
|
||||
/* We call the unfollow on the object, not on the follow record.
|
||||
This is so that we can handle object-specific actions (e.g. unfollow a club unfollow club nodes)
|
||||
It's a bit circular, but we'll live. */
|
||||
if( $item = $follow->_item )
|
||||
{
|
||||
$item->unfollow( Member::loggedIn(), $follow->id );
|
||||
|
||||
/* Get thing */
|
||||
$thing = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
$thing = $class::loadAndCheckPerms( (int) $follow['follow_rel_id'] );
|
||||
|
||||
foreach ( $thing->nodes() as $node )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . Member::loggedIn()->member_id ), Member::loggedIn()->member_id ) );
|
||||
}
|
||||
}
|
||||
catch ( OutOfRangeException $e ) {}
|
||||
}
|
||||
if ( DataLayer::enabled( 'analytics_full' ) )
|
||||
{
|
||||
DataLayer::i()->addEvent( 'unfollow', [
|
||||
'follow_app' => $follow->app,
|
||||
'follow_area' => $follow->area,
|
||||
'follow_id' => $follow->rel_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( Request::i()->isAjax() )
|
||||
{
|
||||
@@ -898,7 +803,7 @@ class notifications extends Controller
|
||||
protected function unfollowFromEmail() : void
|
||||
{
|
||||
/* Logged in? */
|
||||
if ( Member::loggedIn()->member_id )
|
||||
if ( Member::loggedIn()->member_id and ! isset( Request::i()->listunsubscribe ) )
|
||||
{
|
||||
/* Go to the normal page */
|
||||
Output::i()->redirect( Url::internal( "app=core&module=system&controller=notifications&do=follow&follow_app=". Request::i()->follow_app ."&follow_area=". Request::i()->follow_area ."&follow_id=" . Request::i()->follow_id ) );
|
||||
@@ -906,12 +811,12 @@ class notifications extends Controller
|
||||
|
||||
if ( ! empty( Request::i()->gkey ) )
|
||||
{
|
||||
list( $followKey, $memberKey ) = explode( '-', Request::i()->gkey );
|
||||
[ $followKey, $memberKey ] = explode( '-', Request::i()->gkey );
|
||||
/* Do we follow it? */
|
||||
try
|
||||
{
|
||||
$current = Db::i()->select( '*', 'core_follow', array( 'MD5( CONCAT_WS( \';\', follow_app, follow_area, follow_rel_id, follow_member_id, follow_added ) )=?', $followKey ) )->first();
|
||||
|
||||
|
||||
/* Already no subs? */
|
||||
if ( $current['follow_notify_freq'] === 'none' )
|
||||
{
|
||||
@@ -929,29 +834,8 @@ class notifications extends Controller
|
||||
{
|
||||
throw new Exception;
|
||||
}
|
||||
|
||||
/* Get class */
|
||||
if( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'member' )
|
||||
{
|
||||
$class = 'IPS\\Member';
|
||||
}
|
||||
elseif( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'club' )
|
||||
{
|
||||
$class = 'IPS\\Member\Club';
|
||||
}
|
||||
elseif( Request::i()->follow_app == 'core' and Request::i()->follow_area == 'tag' )
|
||||
{
|
||||
$class = 'IPS\Content\Tag';
|
||||
}
|
||||
else
|
||||
{
|
||||
$class = 'IPS\\' . Request::i()->follow_app . '\\' . IPS::mb_ucfirst( Request::i()->follow_area );
|
||||
|
||||
if( !IPS::classUsesTrait( $class, 'IPS\Content\Followable' ) )
|
||||
{
|
||||
throw new Exception;
|
||||
}
|
||||
}
|
||||
|
||||
$class = Follow::getClassToFollow( Request::i()->follow_app, Request::i()->follow_area );
|
||||
if ( !class_exists( $class ) )
|
||||
{
|
||||
throw new Exception;
|
||||
@@ -987,7 +871,18 @@ class notifications extends Controller
|
||||
|
||||
/* Grab a count */
|
||||
$count = Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_member_id=? and follow_notify_freq != ?', $member->member_id, 'none' ) )->first();
|
||||
|
||||
|
||||
if ( isset( Request::i()->listunsubscribe ) and Request::i()->requestMethod() == 'POST' )
|
||||
{
|
||||
/* Unsubscribe them now as it's come from clicking the list-unsubscribe header in the email which
|
||||
contains List-Unsubscribe-Post:List-Unsubscribe=One-Click which sends via POST to prevent links being crawled and activated */
|
||||
$thing->follow( 'none', !$current['follow_is_anon'], $member );
|
||||
|
||||
/* Done */
|
||||
Output::i()->output = Member::loggedIn()->language()->addToStack('unsubscribed');
|
||||
return;
|
||||
}
|
||||
|
||||
$form = new Form( 'unfollowFromEmail', 'update_follow' );
|
||||
$form->class = 'ipsForm--vertical ipsForm--notifications-unfollow-email';
|
||||
|
||||
@@ -1014,20 +909,7 @@ class notifications extends Controller
|
||||
{
|
||||
if ( $values['guest_unfollow_choice'] == 'single' or isset( $values['guest_unfollow_single'] ) )
|
||||
{
|
||||
Db::i()->update( 'core_follow', array( 'follow_notify_freq' => 'none' ), array( 'follow_id=? AND follow_member_id=?', $current['follow_id'], $member->member_id ) );
|
||||
|
||||
/* Unfollow club areas */
|
||||
if ( $class == "IPS\Member\Club" )
|
||||
{
|
||||
foreach ( $thing->nodes() as $node )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
Db::i()->update( 'core_follow', array( 'follow_notify_freq' => 'none' ), array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . $member->member_id ), $member->member_id ) );
|
||||
}
|
||||
}
|
||||
$thing->follow( 'none', !$current['follow_is_anon'], $member );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1196,9 +1078,15 @@ class notifications extends Controller
|
||||
Output::i()->json( array( 'error' => 'not_found' ), 404 );
|
||||
}
|
||||
|
||||
/* Is this our notification? */
|
||||
$data = json_decode( $notification['notification_data'], true );
|
||||
|
||||
if ( ! is_array( $data ) )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'invalid_data' ), 400 );
|
||||
}
|
||||
|
||||
$data['unreadCount'] = Member::loggedIn()->notification_cnt;
|
||||
|
||||
if( !isset( $data['member'] ) OR $data['member'] != Member::loggedIn()->member_id )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'no_permission' ), 403 );
|
||||
|
||||
Reference in new issue
Block a user