Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

@@ -11,16 +11,44 @@
namespace IPS\core\modules\front\clubs;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\Application;
use IPS\Content\Search\Query;
use IPS\core\Stream;
use IPS\DateTime;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\GeoLocation;
use IPS\Helpers\Form;
use IPS\Helpers\Form\CheckboxSet;
use IPS\Helpers\Form\Radio;
use IPS\Http\Url;
use IPS\Member;
use IPS\Member\Club;
use IPS\Member\Club\CustomField;
use IPS\Output;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use function count;
use function defined;
use function floatval;
use function in_array;
use function intval;
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;
}
/**
* Clubs List
*/
class _directory extends \IPS\Dispatcher\Controller
class directory extends Controller
{
/**
@@ -30,7 +58,7 @@ class _directory extends \IPS\Dispatcher\Controller
'community_area' => array( 'value' => 'search', 'odkUpdate' => 'true' )
* )
*/
public static $dataLayerContext = array(
public static array $dataLayerContext = array(
'community_area' => [ 'value' => 'clubs', 'odkUpdate' => 'true']
);
@@ -39,26 +67,22 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function execute()
public function execute() : void
{
/* Permission check */
if ( !\IPS\Settings::i()->clubs )
if ( !Settings::i()->clubs )
{
\IPS\Output::i()->error( 'no_module_permission', '2C349/2', 403, '' );
Output::i()->error( 'no_module_permission', '2C349/2', 403, '' );
}
/* CSS */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/clubs.css', 'core', 'front' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/clubs_responsive.css', 'core', 'front' ) );
}
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/clubs.css', 'core', 'front' ) );
/* JS */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_clubs.js', 'core', 'front' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front_clubs.js', 'core', 'front' ) );
/* Location for online list */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), array(), 'loc_clubs_directory' );
Session::i()->setLocation( Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), array(), 'loc_clubs_directory' );
/* Pass up */
parent::execute();
@@ -69,33 +93,33 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$baseUrl = \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' );
$baseUrl = Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' );
/* Display */
$view = \IPS\Settings::i()->clubs_default_view;
if ( \IPS\Settings::i()->clubs_allow_view_change )
$view = Settings::i()->clubs_default_view;
if ( Settings::i()->clubs_allow_view_change )
{
if ( isset( \IPS\Request::i()->view ) and \in_array( \IPS\Request::i()->view, array( 'grid', 'list' ) ) )
if ( isset( Request::i()->view ) and in_array( Request::i()->view, array( 'grid', 'list' ) ) )
{
\IPS\Session::i()->csrfCheck();
\IPS\Request::i()->setCookie( 'clubs_view', \IPS\Request::i()->view, \IPS\DateTime::create()->add( new \DateInterval('P1Y' ) ) );
$view = \IPS\Request::i()->view;
Session::i()->csrfCheck();
Request::i()->setCookie( 'clubs_view', Request::i()->view, DateTime::create()->add( new DateInterval('P1Y' ) ) );
$view = Request::i()->view;
\IPS\Output::i()->redirect( $baseUrl );
Output::i()->redirect( $baseUrl );
}
elseif ( isset( \IPS\Request::i()->cookie['clubs_view'] ) and \in_array( \IPS\Request::i()->cookie['clubs_view'], array( 'grid', 'list' ) ) )
elseif ( isset( Request::i()->cookie['clubs_view'] ) and in_array( Request::i()->cookie['clubs_view'], array( 'grid', 'list' ) ) )
{
$view = \IPS\Request::i()->cookie['clubs_view'];
$view = Request::i()->cookie['clubs_view'];
}
}
/* All Clubs: Sort */
$sortOption = \IPS\Settings::i()->clubs_default_sort;
if ( isset( \IPS\Request::i()->sort ) and \in_array( \IPS\Request::i()->sort, array( 'last_activity', 'members', 'content', 'created', 'name' ) ) )
$sortOption = Settings::i()->clubs_default_sort;
if ( isset( Request::i()->sort ) and in_array( Request::i()->sort, array( 'last_activity', 'members', 'content', 'created', 'name' ) ) )
{
$sortOption = \IPS\Request::i()->sort;
$sortOption = Request::i()->sort;
$baseUrl = $baseUrl->setQueryString( 'sort', $sortOption );
}
@@ -104,16 +128,16 @@ class _directory extends \IPS\Dispatcher\Controller
$filters = array();
$mineOnly = FALSE;
$extraWhere = NULL;
if ( isset( \IPS\Request::i()->filter ) and \IPS\Request::i()->filter === 'mine' AND \IPS\Member::loggedIn()->member_id )
if ( isset( Request::i()->filter ) and Request::i()->filter === 'mine' AND Member::loggedIn()->member_id )
{
$mineOnly = TRUE;
$baseUrl = $baseUrl->setQueryString( 'filter', 'mine' );
}
if ( isset( \IPS\Request::i()->type ) and \in_array( \IPS\Request::i()->type, array( 'free', 'paid' ) ) )
if ( isset( Request::i()->type ) and in_array( Request::i()->type, array( 'free', 'paid' ) ) )
{
$baseUrl = $baseUrl->setQueryString( 'type', \IPS\Request::i()->type );
$baseUrl = $baseUrl->setQueryString( 'type', Request::i()->type );
if ( \IPS\Request::i()->type === 'free' )
if ( Request::i()->type === 'free' )
{
$extraWhere[] = array( 'fee IS NULL' );
}
@@ -122,19 +146,19 @@ class _directory extends \IPS\Dispatcher\Controller
$extraWhere[] = array( 'fee IS NOT NULL' );
}
}
foreach ( \IPS\Member\Club\CustomField::fields() as $field )
foreach ( CustomField::fields() as $field )
{
$k = "f{$field->id}";
if ( $field->filterable and isset( \IPS\Request::i()->$k ) )
if ( $field->filterable and isset( Request::i()->$k ) )
{
switch ( $field->type )
{
case 'Checkbox':
case 'YesNo':
if ( \in_array( \IPS\Request::i()->$k, array( 0, 1 ) ) )
if ( in_array( Request::i()->$k, array( 0, 1 ) ) )
{
$filters[ $field->id ] = \IPS\Request::i()->$k;
$baseUrl = $baseUrl->setQueryString( 'f' . $field->id, \IPS\Request::i()->$k );
$filters[ $field->id ] = Request::i()->$k;
$baseUrl = $baseUrl->setQueryString( 'f' . $field->id, Request::i()->$k );
}
break;
@@ -142,7 +166,7 @@ class _directory extends \IPS\Dispatcher\Controller
case 'Radio':
case 'Select':
$options = json_decode( $field->extra, TRUE );
foreach ( \IPS\Request::i()->$k as $id )
foreach ( Request::i()->$k as $id )
{
if ( isset( $options[ $id ] ) )
{
@@ -167,28 +191,28 @@ class _directory extends \IPS\Dispatcher\Controller
}
/* Get Featured Clubs */
$featuredClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), NULL, 'RAND()', FALSE, array(), 'featured=1' );
$featuredClubs = Club::clubs( Member::loggedIn(), NULL, 'RAND()', FALSE, array(), 'featured=1' );
/* Get All Clubs */
$perPage = 24;
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
$page = isset( Request::i()->page ) ? intval( Request::i()->page ) : 1;
if( $page < 1 )
{
$page = 1;
}
$clubsCount = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), $sortOption, $mineOnly, $filters, $extraWhere, TRUE );
$allClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), $sortOption, $mineOnly, $filters, $extraWhere );
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, ceil( $clubsCount / $perPage ), $page, $perPage );
$clubsCount = Club::clubs( Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), $sortOption, $mineOnly, $filters, $extraWhere, TRUE );
$allClubs = Club::clubs( Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), $sortOption, $mineOnly, $filters, $extraWhere );
$pagination = Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, ceil( $clubsCount / $perPage ), $page, $perPage );
/* Get my clubs and invites */
$myClubsActivity = NULL;
$myClubsInvites = NULL;
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
$myClubsActivity = new \IPS\core\Stream;
$myClubsActivity = $myClubsActivity->query()->filterByClub( \IPS\Member::loggedIn()->clubs() )->setLimit(6)->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_UPDATED )->search();
$myClubsInvites = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_clubs_memberships', array( 'member_id=? and ( status=? or status=? )', \IPS\Member::loggedIn()->member_id, \IPS\Member\Club::STATUS_REQUESTED, \IPS\Member\Club::STATUS_INVITED )
$myClubsActivity = new Stream;
$myClubsActivity = $myClubsActivity->query()->filterByClub( Member::loggedIn()->clubs() )->setLimit(6)->setOrder( Query::ORDER_NEWEST_UPDATED )->search();
$myClubsInvites = new ActiveRecordIterator( Db::i()->select( '*', 'core_clubs_memberships', array( 'member_id=? and ( status=? or status=? )', Member::loggedIn()->member_id, Club::STATUS_REQUESTED, Club::STATUS_INVITED )
)->join(
'core_clubs',
"core_clubs_memberships.club_id=core_clubs.id"
@@ -197,62 +221,62 @@ class _directory extends \IPS\Dispatcher\Controller
/* Build Map */
$mapMarkers = NULL;
if ( \IPS\GeoLocation::enabled() )
if ( GeoLocation::enabled() )
{
$mapMarkers = array();
$where = array( array( 'location_lat IS NOT NULL' ) );
if ( !\IPS\Member::loggedIn()->member_id )
if ( !Member::loggedIn()->member_id )
{
$where[] = array( "type<>?", \IPS\Member\Club::TYPE_PRIVATE );
$where[] = array( "type<>?", Club::TYPE_PRIVATE );
}
elseif ( !\IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
elseif ( !Member::loggedIn()->modPermission('can_access_all_clubs') )
{
$where[] = array( "( type<>? OR status IN('" . \IPS\Member\Club::STATUS_MEMBER . "','" . \IPS\Member\Club::STATUS_MODERATOR . "','" . \IPS\Member\Club::STATUS_LEADER . "','" . \IPS\Member\Club::STATUS_EXPIRED . "','" . \IPS\Member\Club::STATUS_EXPIRED_MODERATOR . "') )", \IPS\Member\Club::TYPE_PRIVATE );
$where[] = array( "( type<>? OR status IN('" . Club::STATUS_MEMBER . "','" . Club::STATUS_MODERATOR . "','" . Club::STATUS_LEADER . "','" . Club::STATUS_EXPIRED . "','" . Club::STATUS_EXPIRED_MODERATOR . "') )", Club::TYPE_PRIVATE );
}
$select = \IPS\Db::i()->select( array( 'id', 'name', 'location_lat', 'location_long' ), 'core_clubs', $where );
if ( \IPS\Member::loggedIn()->member_id )
$select = Db::i()->select( array( 'id', 'name', 'location_lat', 'location_long' ), 'core_clubs', $where );
if ( Member::loggedIn()->member_id )
{
$select->join( 'core_clubs_memberships', array( 'club_id=id AND member_id=?', \IPS\Member::loggedIn()->member_id ) );
$select->join( 'core_clubs_memberships', array( 'club_id=id AND member_id=?', Member::loggedIn()->member_id ) );
}
foreach ( $select as $club )
{
$mapMarkers[ $club['id'] ] = array(
'lat' => \floatval( $club['location_lat'] ),
'long' => \floatval( $club['location_long'] ),
'lat' => floatval( $club['location_lat'] ),
'long' => floatval( $club['location_long'] ),
'title' => $club['name']
);
}
}
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
\IPS\Output::i()->sidebar['contextual'] = \IPS\Theme::i()->getTemplate('clubs')->myClubsSidebar( \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), 10, 'last_activity', TRUE ), $myClubsActivity, $myClubsInvites );
Output::i()->sidebar['contextual'] = Theme::i()->getTemplate('clubs')->myClubsSidebar( Club::clubs( Member::loggedIn(), 10, 'last_activity', TRUE ), $myClubsActivity, $myClubsInvites );
/* Prime the cache */
$allClubs = iterator_to_array( $allClubs );
if( \count( $allClubs ) )
if( count( $allClubs ) )
{
foreach( $allClubs as $clubId => $clubData )
{
if( !isset( $allClubs[ $clubId ]->memberStatuses[ \IPS\Member::loggedIn()->member_id ] ) )
if( !isset( $clubData->memberStatuses[ Member::loggedIn()->member_id ] ) )
{
$allClubs[ $clubId ]->memberStatuses[ \IPS\Member::loggedIn()->member_id ] = NULL;
$clubData->memberStatuses[ Member::loggedIn()->member_id ] = NULL;
}
}
foreach( \IPS\Db::i()->select( '*', 'core_clubs_memberships', array( 'member_id=? AND ' . \IPS\Db::i()->in( 'club_id', array_keys( $allClubs ) ), \IPS\Member::loggedIn()->member_id ) ) as $clubMembership )
foreach( Db::i()->select( '*', 'core_clubs_memberships', array( 'member_id=? AND ' . Db::i()->in( 'club_id', array_keys( $allClubs ) ), Member::loggedIn()->member_id ) ) as $clubMembership )
{
$allClubs[ $clubMembership['club_id'] ]->memberStatuses[ \IPS\Member::loggedIn()->member_id ] = $clubMembership['status'];
$allClubs[ $clubMembership['club_id'] ]->memberStatuses[ Member::loggedIn()->member_id ] = $clubMembership['status'];
}
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('clubs')->directory( $featuredClubs, $allClubs, $pagination, $baseUrl, $sortOption, $myClubsActivity, $mapMarkers, $view, $mineOnly );
Output::i()->title = Member::loggedIn()->language()->addToStack('module__core_clubs');
Output::i()->output = Theme::i()->getTemplate('clubs')->directory( $featuredClubs, $allClubs, $pagination, $baseUrl, $sortOption, $myClubsActivity, $mapMarkers, $view, $mineOnly );
}
/**
@@ -260,23 +284,23 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function filters()
protected function filters() : void
{
$fields = \IPS\Member\Club\CustomField::roots();
$fields = CustomField::roots();
$form = new \IPS\Helpers\Form('filter', 'filter');
$form = new Form('filter', 'filter');
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
$form->add( new \IPS\Helpers\Form\Radio( 'club_filter_type', isset( \IPS\Request::i()->filter ) ? \IPS\Request::i()->filter : 'all', TRUE, array( 'options' => array(
$form->add( new Radio( 'club_filter_type', isset( Request::i()->filter ) ? Request::i()->filter : 'all', TRUE, array( 'options' => array(
'all' => 'all_clubs',
'mine' => 'my_clubs'
) ) ) );
}
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
if ( Application::appIsEnabled( 'nexus' ) and Settings::i()->clubs_paid_on )
{
$form->add( new \IPS\Helpers\Form\Radio( 'club_membership_fee', isset( \IPS\Request::i()->type ) ? \IPS\Request::i()->type : 'all', TRUE, array( 'options' => array(
$form->add( new Radio( 'club_membership_fee', isset( Request::i()->type ) ? Request::i()->type : 'all', TRUE, array( 'options' => array(
'all' => 'all_clubs',
'free' => 'club_membership_free',
'paid' => 'club_membership_paid'
@@ -292,7 +316,7 @@ class _directory extends \IPS\Dispatcher\Controller
{
case 'Checkbox':
case 'YesNo':
$input = new \IPS\Helpers\Form\CheckboxSet( 'field_' . $field->id, isset( \IPS\Request::i()->$k ) ? \IPS\Request::i()->$k : array( 1, 0 ), FALSE, array( 'options' => array(
$input = new CheckboxSet( 'field_' . $field->id, isset( Request::i()->$k ) ? Request::i()->$k : array( 1, 0 ), FALSE, array( 'options' => array(
1 => 'yes',
0 => 'no',
) ) );
@@ -304,7 +328,7 @@ class _directory extends \IPS\Dispatcher\Controller
case 'Radio':
case 'Select':
$options = json_decode( $field->extra, TRUE );
$input = new \IPS\Helpers\Form\CheckboxSet( 'field_' . $field->id, isset( \IPS\Request::i()->$k ) ? \IPS\Request::i()->$k : array_keys( $options ), FALSE, array( 'options' => $options ) );
$input = new CheckboxSet( 'field_' . $field->id, isset( Request::i()->$k ) ? Request::i()->$k : array_keys( $options ), FALSE, array( 'options' => $options ) );
$input->label = $field->_title;
$form->add( $input );
break;
@@ -314,14 +338,14 @@ class _directory extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
$url = \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' );
$url = Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' );
if ( \IPS\Member::loggedIn()->member_id and $values['club_filter_type'] === 'mine' )
if ( Member::loggedIn()->member_id and $values['club_filter_type'] === 'mine' )
{
$url = $url->setQueryString( 'filter', 'mine' );
}
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on and $values['club_membership_fee'] !== 'all' )
if ( Application::appIsEnabled( 'nexus' ) and Settings::i()->clubs_paid_on and $values['club_membership_fee'] !== 'all' )
{
$url = $url->setQueryString( 'type', $values['club_membership_fee'] );
}
@@ -334,7 +358,7 @@ class _directory extends \IPS\Dispatcher\Controller
{
case 'Checkbox':
case 'YesNo':
if ( \count( $values[ 'field_' . $field->id ] ) === 1 )
if ( count( $values[ 'field_' . $field->id ] ) === 1 )
{
$url = $url->setQueryString( 'f' . $field->id, array_pop( $values[ 'field_' . $field->id ] ) );
}
@@ -344,7 +368,7 @@ class _directory extends \IPS\Dispatcher\Controller
case 'Radio':
case 'Select':
$options = json_decode( $field->extra, TRUE );
if ( \count( $values[ 'field_' . $field->id ] ) > 0 and \count( $values[ 'field_' . $field->id ] ) < \count( $options ) )
if ( count( $values[ 'field_' . $field->id ] ) > 0 and count( $values[ 'field_' . $field->id ] ) < count( $options ) )
{
$url = $url->setQueryString( 'f' . $field->id, $values[ 'field_' . $field->id ] );
}
@@ -353,16 +377,16 @@ class _directory extends \IPS\Dispatcher\Controller
}
}
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
else
{
\IPS\Output::i()->output = $form;
Output::i()->output = $form;
}
}
@@ -371,12 +395,12 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function create()
protected function create() : void
{
$availableTypes = array();
if ( \IPS\Member::loggedIn()->member_id ) // Guests can't create any type of clubs
if ( Member::loggedIn()->member_id ) // Guests can't create any type of clubs
{
foreach ( explode( ',', \IPS\Member::loggedIn()->group['g_create_clubs'] ) as $type )
foreach ( explode( ',', Member::loggedIn()->group['g_create_clubs'] ) as $type )
{
if ( $type !== '' )
{
@@ -386,32 +410,32 @@ class _directory extends \IPS\Dispatcher\Controller
}
if ( !$availableTypes )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->member_id ? 'no_module_permission' : 'no_module_permission_guest', '2C349/1', 403, '' );
Output::i()->error( Member::loggedIn()->member_id ? 'no_module_permission' : 'no_module_permission_guest', '2C349/1', 403, '' );
}
if ( \IPS\Member::loggedIn()->group['g_club_limit'] )
if ( Member::loggedIn()->group['g_club_limit'] )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', array( 'owner=?', \IPS\Member::loggedIn()->member_id ) )->first() >= \IPS\Member::loggedIn()->group['g_club_limit'] )
if ( Db::i()->select( 'COUNT(*)', 'core_clubs', array( 'owner=?', Member::loggedIn()->member_id ) )->first() >= Member::loggedIn()->group['g_club_limit'] )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'too_many_clubs', FALSE, array( 'pluralize' => array( \IPS\Member::loggedIn()->group['g_club_limit'] ) ) ), '2C349/3', 403, '' );
Output::i()->error( Member::loggedIn()->language()->addToStack( 'too_many_clubs', FALSE, array( 'pluralize' => array( Member::loggedIn()->group['g_club_limit'] ) ) ), '2C349/3', 403, '' );
}
}
$club = new \IPS\Member\Club;
$club = new Club;
if ( $form = $club->form( FALSE, TRUE, $availableTypes ) )
{
if( $values = $form->values() )
{
$club->processForm( $values, FALSE, TRUE, $availableTypes );
\IPS\Output::i()->redirect( $club->url() );
Output::i()->redirect( $club->url() );
}
$form->class = 'ipsForm_vertical';
$form->class = 'ipsForm--vertical ipsForm--clubs-directory';
}
else
{
if ( !$club->approved and \IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
if ( !$club->approved and Member::loggedIn()->modPermission('can_access_all_clubs') )
{
$club->approved = TRUE;
$club->save();
@@ -419,20 +443,20 @@ class _directory extends \IPS\Dispatcher\Controller
if( $club->approved )
{
\IPS\Member::loggedIn()->achievementAction( 'core', 'NewClub', $club );
Member::loggedIn()->achievementAction( 'core', 'NewClub', $club );
}
\IPS\Output::i()->redirect( $club->url() );
Output::i()->redirect( $club->url() );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('create_club');
if( \IPS\Request::i()->isAjax() )
Output::i()->title = Member::loggedIn()->language()->addToStack('create_club');
if( Request::i()->isAjax() )
{
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'clubs' )->create( $form );
Output::i()->output = Theme::i()->getTemplate( 'clubs' )->create( $form );
}
}
}
+55 -38
View File
@@ -12,27 +12,44 @@
namespace IPS\core\modules\front\clubs;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\GeoLocation;
use IPS\Helpers\Form;
use IPS\Http\Url;
use IPS\Member;
use IPS\Member\Club;
use IPS\Member\Club\Page as ClubPage;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use OutOfRangeException;
use function defined;
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;
}
/**
* page
*/
class _page extends \IPS\Dispatcher\Controller
class page extends Controller
{
/**
* @brief Page
*/
protected $page;
protected ?ClubPage $page = null;
/**
* @brief These properties are used to specify datalayer context properties.
*
*/
public static $dataLayerContext = array(
public static array $dataLayerContext = array(
'community_area' => [ 'value' => 'clubs', 'odkUpdate' => 'true']
);
@@ -41,41 +58,41 @@ class _page extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function execute()
public function execute() : void
{
try
{
$this->page = \IPS\Member\Club\Page::loadAndCheckPerms( \IPS\Request::i()->id );
$this->page = ClubPage::loadAndCheckPerms( Request::i()->id );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2S410/4', 404, '' );
Output::i()->error( 'node_error', '2S410/4', 404, '' );
}
\IPS\Output::i()->sidebar['contextual'] = '';
Output::i()->sidebar['contextual'] = '';
/* Club info in sidebar */
if ( \IPS\Settings::i()->clubs_header == 'sidebar' )
if ( Settings::i()->clubs_header == 'sidebar' )
{
\IPS\Output::i()->sidebar['contextual'] .= \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->header( $this->page->club, NULL, 'sidebar', $this->page );
Output::i()->sidebar['contextual'] .= Theme::i()->getTemplate( 'clubs', 'core' )->header( $this->page->club, NULL, 'sidebar', $this->page );
}
if( ( \IPS\GeoLocation::enabled() and \IPS\Settings::i()->clubs_locations AND $location = $this->page->club->location() ) )
if( ( GeoLocation::enabled() and Settings::i()->clubs_locations AND $location = $this->page->club->location() ) )
{
\IPS\Output::i()->sidebar['contextual'] .= \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->clubLocationBox( $this->page->club, $location );
Output::i()->sidebar['contextual'] .= Theme::i()->getTemplate( 'clubs', 'core' )->clubLocationBox( $this->page->club, $location );
}
if( $this->page->club->type != \IPS\Member\Club::TYPE_PUBLIC AND $this->page->club->canViewMembers() )
if( $this->page->club->type != Club::TYPE_PUBLIC AND $this->page->club->canViewMembers() )
{
\IPS\Output::i()->sidebar['contextual'] .= \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->clubMemberBox( $this->page->club );
Output::i()->sidebar['contextual'] .= Theme::i()->getTemplate( 'clubs', 'core' )->clubMemberBox( $this->page->club );
}
\IPS\Output::i()->breadcrumb[] = array( $this->page->club->url(), $this->page->club->name );
\IPS\Output::i()->breadcrumb[] = array( NULL, $this->page->title );
Output::i()->breadcrumb[] = array( $this->page->club->url(), $this->page->club->name );
Output::i()->breadcrumb[] = array( NULL, $this->page->title );
if( !$this->page->meta_index )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
Output::i()->metaTags['robots'] = 'noindex';
}
parent::execute();
@@ -86,15 +103,15 @@ class _page extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
if ( !$this->page->club->rulesAcknowledged() )
{
\IPS\Http\Url::internal( $this->page->club->url()->setQueryString( 'do', 'rules' ) );
Url::internal( $this->page->club->url()->setQueryString( 'do', 'rules' ) );
}
\IPS\Output::i()->title = $this->page->title;
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'clubs' )->viewPage( $this->page );
Output::i()->title = $this->page->title;
Output::i()->output = Theme::i()->getTemplate( 'clubs' )->viewPage( $this->page );
}
/**
@@ -102,17 +119,17 @@ class _page extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function edit()
public function edit() : void
{
if( !$this->page->canEdit() )
{
\IPS\Output::i()->error( 'node_noperm_edit', '2S410/1', 403, '' );
Output::i()->error( 'node_noperm_edit', '2S410/1', 403, '' );
}
/* Init form */
$form = new \IPS\Helpers\Form;
$form->class = 'ipsForm_vertical';
\IPS\Member\Club\Page::form( $form, $this->page->club, $this->page );
$form = new Form;
$form->class = 'ipsForm--vertical ipsForm--edit-club-page';
ClubPage::form( $form, $this->page->club, $this->page );
/* Form Submission */
if ( $values = $form->values() )
@@ -120,20 +137,20 @@ class _page extends \IPS\Dispatcher\Controller
$this->page->formatFormValues( $values );
$this->page->save();
\IPS\File::claimAttachments( "club-page-{$this->page->id}", $this->page->id );
File::claimAttachments( "club-page-{$this->page->id}", $this->page->id );
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
Output::i()->json( 'OK' );
}
else
{
\IPS\Output::i()->redirect( $this->page->url() );
Output::i()->redirect( $this->page->url() );
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( "edit" );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
Output::i()->title = Member::loggedIn()->language()->addToStack( "edit" );
Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
/**
@@ -141,17 +158,17 @@ class _page extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function delete()
public function delete() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
if( !$this->page->canDelete() )
{
\IPS\Output::i()->error( 'node_noperm_delete', '2S410/2', 403, '' );
Output::i()->error( 'node_noperm_delete', '2S410/2', 403, '' );
}
$this->page->delete();
\IPS\Output::i()->redirect( $this->page->club->url(), 'deleted' );
Output::i()->redirect( $this->page->club->url(), 'deleted' );
}
}
File diff suppressed because it is too large. Load diff
@@ -11,36 +11,55 @@
namespace IPS\core\modules\front\contact;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Captcha;
use IPS\Helpers\Form\Editor;
use IPS\Helpers\Form\Email;
use IPS\Email as EmailClass;
use IPS\Helpers\Form\Text;
use IPS\Login;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use IPS\Theme;
use UnderflowException;
use function defined;
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;
}
/**
* Contact Form
*/
class _contact extends \IPS\Dispatcher\Controller
class contact extends Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
public bool $isContentPage = FALSE;
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
if ( !\IPS\Member::loggedIn()->canUseContactUs() )
if ( !Member::loggedIn()->canUseContactUs() )
{
\IPS\Output::i()->error( 'no_module_permission', '2S333/1', 403, '' );
Output::i()->error( 'no_module_permission', '2S333/1', 403, '' );
}
/* Execute */
return parent::execute();
parent::execute();
}
/**
@@ -48,33 +67,33 @@ class _contact extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Get extensions */
$extensions = \IPS\Application::allExtensions( 'core', 'ContactUs', FALSE, 'core', 'InternalEmail', TRUE );
$extensions = Application::allExtensions( 'core', 'ContactUs', FALSE, 'core', 'InternalEmail', TRUE );
/* Don't let robots index this page, it has no value */
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->metaTags['robots'] = 'noindex';
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
$form = new \IPS\Helpers\Form( 'contact', 'send' );
$form->hiddenValues['contact_referrer'] = (string) \IPS\Request::i()->referrer();
$form->class = 'ipsForm_vertical';
$form = new Form( 'contact', 'send' );
$form->hiddenValues['contact_referrer'] = (string) Request::i()->referrer();
$form->class = 'ipsForm--vertical ipsForm--contact';
$form->add( new \IPS\Helpers\Form\Editor( 'contact_text', NULL, TRUE, array(
$form->add( new Editor( 'contact_text', NULL, TRUE, array(
'app' => 'core',
'key' => 'Contact',
'autoSaveKey' => 'contact-' . \IPS\Member::loggedIn()->member_id,
'autoSaveKey' => 'contact-' . Member::loggedIn()->member_id,
) ) );
if ( !\IPS\Member::loggedIn()->member_id )
if ( !Member::loggedIn()->member_id )
{
$form->add( new \IPS\Helpers\Form\Text( 'contact_name', NULL, TRUE ) );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
if ( \IPS\Settings::i()->bot_antispam_type !== 'none' )
$form->add( new Text( 'contact_name', NULL, TRUE ) );
$form->add( new Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => Text::BYPASS_PROFANITY_ALL ) ) );
if ( Settings::i()->bot_antispam_type !== 'none' and Settings::i()->guest_captcha )
{
$form->add( new \IPS\Helpers\Form\Captcha );
$form->add( new Captcha );
}
}
foreach ( $extensions as $k => $class )
@@ -84,25 +103,25 @@ class _contact extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
if ( !\IPS\Member::loggedIn()->member_id AND \IPS\Settings::i()->contact_email_verify )
if ( ! Member::loggedIn()->member_id AND Settings::i()->contact_email_verify )
{
$key = \IPS\Login::generateRandomString( 32 );
\IPS\Db::i()->insert( 'core_contact_verify', array(
$key = Login::generateRandomString( 32 );
Db::i()->insert( 'core_contact_verify', array(
'email_address' => $values['email_address'],
'contact_data' => json_encode( $values ),
'verify_key' => $key
), TRUE );
$email = \IPS\Email::buildFromTemplate( 'core', 'contact_verify', array( $values['email_address'], $key ), \IPS\Email::TYPE_TRANSACTIONAL );
$email = EmailClass::buildFromTemplate( 'core', 'contact_verify', array( $values['email_address'], $key ), EmailClass::TYPE_TRANSACTIONAL );
$email->send( $values['email_address'] );
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
Output::i()->json( 'OK' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact_verify' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactVerify();
Output::i()->title = Member::loggedIn()->language()->addToStack( 'contact_verify' );
Output::i()->output = Theme::i()->getTemplate( 'system' )->contactVerify();
return;
}
@@ -114,18 +133,18 @@ class _contact extends \IPS\Dispatcher\Controller
}
}
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
Output::i()->json( 'OK' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'message_sent' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactDone();
Output::i()->title = Member::loggedIn()->language()->addToStack( 'message_sent' );
Output::i()->output = Theme::i()->getTemplate( 'system' )->contactDone();
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contact( $form );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'contact' );
Output::i()->output = Theme::i()->getTemplate( 'system' )->contact( $form );
}
}
@@ -134,36 +153,36 @@ class _contact extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function confirm()
protected function confirm() : void
{
/* Show interstitial page to prevent email clients from auto-verifying. */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
$form = new \IPS\Helpers\Form( 'form', NULL );
$form->class = 'ipsForm_vertical';
$form->actionButtons[] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( 'contact_click_to_verify', 'submit', null, 'ipsButton ipsButton_primary ipsButton_fullWidth', array( 'tabindex' => '2', 'accesskey' => 's' ) );
$form->hiddenValues['email'] = \IPS\Request::i()->email;
$form->hiddenValues['key'] = \IPS\Request::i()->key;
$form = new Form( 'form', NULL );
$form->class = 'ipsForm--vertical ipsForm--contact-confirm';
$form->actionButtons[] = Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( 'contact_click_to_verify', 'submit', null, 'ipsButton ipsButton_primary ipsButton_fullWidth', array( 'tabindex' => '2', 'accesskey' => 's' ) );
$form->hiddenValues['email'] = Request::i()->email;
$form->hiddenValues['key'] = Request::i()->key;
if ( $values = $form->values() )
{
try
{
$verify = \IPS\Db::i()->select( '*', 'core_contact_verify', array( "email_address=?", $values['email'] ) )->first();
$verify = Db::i()->select( '*', 'core_contact_verify', array( "email_address=?", $values['email'] ) )->first();
}
catch( \UnderflowException )
catch( UnderflowException )
{
\IPS\Output::i()->error( 'node_error', '2C435/1', 404, '' );
Output::i()->error( 'node_error', '2C435/1', 404, '' );
}
if ( \IPS\Login::compareHashes( $verify['verify_key'], $values['key'] ) === FALSE )
if ( Login::compareHashes( $verify['verify_key'], $values['key'] ) === FALSE )
{
\IPS\Output::i()->error( 'contact_verify_key_mismatch', '2C435/2', 403, '' );
Output::i()->error( 'contact_verify_key_mismatch', '2C435/2', 403, '' );
}
/* Send it */
$extensions = \IPS\Application::allExtensions( 'core', 'ContactUs', FALSE, 'core', 'InternalEmail', TRUE );
$extensions = Application::allExtensions( 'core', 'ContactUs', FALSE, 'core', 'InternalEmail', TRUE );
foreach( $extensions AS $k => $extension )
{
@@ -173,16 +192,16 @@ class _contact extends \IPS\Dispatcher\Controller
}
}
\IPS\Db::i()->delete( 'core_contact_verify', array( "email_address=?", $values['email'] ) );
Db::i()->delete( 'core_contact_verify', array( "email_address=?", $values['email'] ) );
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'message_sent' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactDone();
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->title = Member::loggedIn()->language()->addToStack( 'message_sent' );
Output::i()->output = Theme::i()->getTemplate( 'system' )->contactDone();
return;
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact_verify' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactConfirmVerify( $form );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'contact_verify' );
Output::i()->output = Theme::i()->getTemplate( 'system' )->contactConfirmVerify( $form );
}
}
@@ -11,100 +11,126 @@
namespace IPS\core\modules\front\discover;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use DateTimeZone;
use Exception;
use IPS\Application;
use IPS\Content\Reaction;
use IPS\Content\Search\ContentFilter;
use IPS\Content\Search\Index;
use IPS\Content\Search\Query;
use IPS\Content\Search\Results;
use IPS\core\DataLayer;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Helpers\Form\DateRange;
use IPS\Http\Url;
use IPS\IPS;
use IPS\Member;
use IPS\Output;
use IPS\Platform\Bridge;
use IPS\Request;
use IPS\Settings;
use IPS\Theme;
use function array_slice;
use function count;
use function defined;
use function in_array;
use function intval;
use function is_numeric;
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;
}
/**
* Most popular things
*/
class _popular extends \IPS\Dispatcher\Controller
class popular extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
/* Ensure that the rep system is enabled and the leaderboard is enabled */
if ( ! \IPS\Settings::i()->reputation_enabled or ! \IPS\Settings::i()->reputation_leaderboard_on )
if ( ! Settings::i()->reputation_enabled or ! Settings::i()->reputation_leaderboard_on )
{
\IPS\Output::i()->error( 'module_no_permission', '2C343/1', 403, '' );
Output::i()->error( 'module_no_permission', '2C343/1', 403, '' );
}
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/search.css' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/leaderboard.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/search_responsive.css' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/leaderboard_responsive.css' ) );
}
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/search.css' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/leaderboard.css' ) );
/* Make sure default tab is first */
$tabs = array( \IPS\Settings::i()->reputation_leaderboard_default_tab );
$tabs = array( Settings::i()->reputation_leaderboard_default_tab );
foreach( array( 'leaderboard', 'history', 'members' ) as $addTab )
{
if ( $addTab != \IPS\Settings::i()->reputation_leaderboard_default_tab )
if ( $addTab != Settings::i()->reputation_leaderboard_default_tab )
{
$tabs[] = $addTab;
}
}
if ( \IPS\Application::appIsEnabled('cloud') and \IPS\cloud\Application::trendingIsEnabled( alwaysOn: FALSE ) )
if ( Application::appIsEnabled('cloud') and \IPS\cloud\Application::featureIsEnabled('trending') )
{
$tabs[] = 'trending';
}
$activeTab = ( isset( \IPS\Request::i()->tab ) and \in_array(\IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : \IPS\Settings::i()->reputation_leaderboard_default_tab;
$activeTab = ( isset( Request::i()->tab ) and in_array(Request::i()->tab, $tabs ) ) ? Request::i()->tab : Settings::i()->reputation_leaderboard_default_tab;
/* Initiate the breadcrumb */
\IPS\Output::i()->breadcrumb = array( array( \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=" . $activeTab, 'front', 'leaderboard_' . $activeTab ), \IPS\Member::loggedIn()->language()->addToStack('leaderboard_title') ) );
Output::i()->breadcrumb = array( array( Url::internal( "app=core&module=discover&controller=popular&tab=" . $activeTab, 'front', 'leaderboard_' . $activeTab ), Member::loggedIn()->language()->addToStack('leaderboard_title') ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_tabs_' . $activeTab );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'leaderboard_tabs_' . $activeTab );
$content = $this->$activeTab();
/* Data Layer Context Property */
if ( \IPS\Settings::i()->core_datalayer_enabled AND ! \IPS\Request::i()->isAjax() )
if ( Settings::i()->core_datalayer_enabled AND ! Request::i()->isAjax() )
{
\IPS\core\DataLayer::i()->addContextProperty( 'community_area', 'leaderboard', true );
DataLayer::i()->addContextProperty( 'community_area', 'leaderboard' );
}
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $content ), 200, 'text/html' );
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $content ) );
}
else
{
/* Display */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('popular')->tabs( $tabs, $activeTab, $content );
Output::i()->output = Theme::i()->getTemplate('popular')->tabs( $tabs, $activeTab, $content );
}
}
/**
* View top members
*
* @return void
* @return string
*/
protected function members()
protected function members() : string
{
/* Get filters */
$filters = array_merge( array( 'overview' => \IPS\Member::loggedIn()->language()->addToStack('overview') ), \IPS\Member::topMembersOptions( \IPS\Member::TOP_MEMBERS_FILTERS ) );
$filters = array_merge( array( 'overview' => Member::loggedIn()->language()->addToStack('overview') ), Member::topMembersOptions( Member::TOP_MEMBERS_FILTERS ) );
$activeFilter = 'overview';
if ( isset( \IPS\Request::i()->filter ) )
if ( isset( Request::i()->filter ) )
{
if ( array_key_exists( \IPS\Request::i()->filter, $filters ) )
if ( array_key_exists( Request::i()->filter, $filters ) )
{
$activeFilter = \IPS\Request::i()->filter;
$activeFilter = Request::i()->filter;
}
else
{
$possibleFilter = 'IPS\\' . str_replace( '_', '\\', \IPS\Request::i()->filter );
$possibleFilter = 'IPS\\' . str_replace( '_', '\\', Request::i()->filter );
if ( array_key_exists( $possibleFilter, $filters ) )
{
$activeFilter = $possibleFilter;
@@ -115,80 +141,80 @@ class _popular extends \IPS\Dispatcher\Controller
/* Get results */
if ( $activeFilter == 'overview' )
{
$output = \IPS\Theme::i()->getTemplate('popular')->topMembersOverview( \IPS\Member::topMembersOptions( \IPS\Member::TOP_MEMBERS_OVERVIEW ) );
$output = Theme::i()->getTemplate('popular')->topMembersOverview( Member::topMembersOptions( Member::TOP_MEMBERS_OVERVIEW ) );
}
else
{
$output = \IPS\Theme::i()->getTemplate('popular')->topMembersResults( $activeFilter, NULL, \IPS\Member::topMembers( $activeFilter, \IPS\Settings::i()->reputation_max_members ) );
$output = Theme::i()->getTemplate('popular')->topMembersResults( $activeFilter, NULL, Member::topMembers( $activeFilter, Settings::i()->reputation_max_members ) );
}
/* Output */
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=members", 'front', 'leaderboard_members' );
if ( \IPS\Request::i()->isAjax() and \IPS\Request::i()->topMembers )
Output::i()->linkTags['canonical'] = (string) Url::internal( "app=core&module=discover&controller=popular&tab=members", 'front', 'leaderboard_members' );
if ( Request::i()->isAjax() and Request::i()->topMembers )
{
\IPS\Output::i()->json( array( 'rows' => $output, 'extraHtml' => $filters[ $activeFilter ] ) );
Output::i()->json( array( 'rows' => $output, 'extraHtml' => $filters[ $activeFilter ] ) );
}
else
{
return \IPS\Theme::i()->getTemplate('popular')->topMembers( \IPS\Http\Url::internal( 'app=core&module=discover&controller=popular&tab=members', 'front', 'leaderboard_members' ), $filters, $activeFilter, $output );
return Theme::i()->getTemplate('popular')->topMembers( Url::internal( 'app=core&module=discover&controller=popular&tab=members', 'front', 'leaderboard_members' ), $filters, $activeFilter, $output );
}
}
/**
* View past leaders
*
* @return void
* @return string
*/
protected function history()
protected function history() : string
{
$table = new \IPS\Helpers\Table\Db( 'core_reputation_leaderboard_history', \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=history", 'front', 'leaderboard_history' ) );
$table = new \IPS\Helpers\Table\Db( 'core_reputation_leaderboard_history', Url::internal( "app=core&module=discover&controller=popular&tab=history", 'front', 'leaderboard_history' ) );
$table->where = array( 'leader_position <= 3' );
$table->limit = 21;
$table->limit = 60;
$table->selects = array( 'leader_member_id, leader_position, leader_rep_total', 'ABS(leader_date - leader_position) as leader_date' );
$table->joins = array( array( 'select' => 'core_members.*', 'from' => 'core_members', 'where' => 'core_reputation_leaderboard_history.leader_member_id=core_members.member_id' ) );
$table->include = array( 'leader_member_id', 'leader_date', 'leader_position', 'leader_rep_total' );
$table->noSort = array( 'leader_member_id', 'leader_position', 'leader_rep_total' );
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'popular', 'core', 'front' ), 'popularTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'popular', 'core', 'front' ), 'popularRows' );
$table->tableTemplate = array( Theme::i()->getTemplate( 'popular', 'core', 'front' ), 'popularTable' );
$table->rowsTemplate = array( Theme::i()->getTemplate( 'popular', 'core', 'front' ), 'popularRows' );
$table->title = 'leaderboard_history';
$table->mainColumn = 'leader_date';
$table->sortBy = $table->sortBy ?: 'leader_date';
$table->sortDirection = $table->sortDirection ?: 'DESC';
/* Like or what? */
if ( \IPS\Content\Reaction::isLikeMode() )
if ( Reaction::isLikeMode() )
{
\IPS\Member::loggedIn()->language()->words['leader_rep_total'] = \IPS\Member::loggedIn()->language()->addToStack( 'leader_rep_total_likes' );
\IPS\Member::loggedIn()->language()->words['leaderboard_history__desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_desc', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_likes') ) ) );
Member::loggedIn()->language()->words['leader_rep_total'] = Member::loggedIn()->language()->addToStack( 'leader_rep_total_likes' );
Member::loggedIn()->language()->words['leaderboard_history__desc'] = Member::loggedIn()->language()->addToStack( 'leaderboard_history_desc', FALSE, array( 'sprintf' => array( Member::loggedIn()->language()->addToStack( 'leaderboard_history_likes') ) ) );
}
else
{
\IPS\Member::loggedIn()->language()->words['leader_rep_total'] = \IPS\Member::loggedIn()->language()->addToStack( 'leader_rep_total_rep' );
\IPS\Member::loggedIn()->language()->words['leaderboard_history__desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_desc', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_rep') ) ) );
Member::loggedIn()->language()->words['leader_rep_total'] = Member::loggedIn()->language()->addToStack( 'leader_rep_total_rep' );
Member::loggedIn()->language()->words['leaderboard_history__desc'] = Member::loggedIn()->language()->addToStack( 'leaderboard_history_desc', FALSE, array( 'sprintf' => array( Member::loggedIn()->language()->addToStack( 'leaderboard_history_rep') ) ) );
}
/* Parsers */
$table->parsers = array(
'leader_member_id' => function( $val, $row )
{
return \IPS\Member::constructFromData( $row );
return Member::constructFromData( $row );
},
'leader_date' => function( $val )
{
return \IPS\DateTime::ts( $val )->setTimezone( new \DateTimeZone( \IPS\Settings::i()->reputation_timezone ) );
return DateTime::ts( $val )->setTimezone( new DateTimeZone( Settings::i()->reputation_timezone ) );
}
);
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=history", 'front', 'leaderboard_history' );
if ( \IPS\Request::i()->isAjax() )
Output::i()->linkTags['canonical'] = (string) Url::internal( "app=core&module=discover&controller=popular&tab=history", 'front', 'leaderboard_history' );
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $table ), 200, 'text/html' );
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $table ) );
}
else
{
if ( $table->page > 1 )
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'title_with_page_number', FALSE, array( 'sprintf' => array( \IPS\Output::i()->title, $table->page ) ) );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'title_with_page_number', FALSE, array( 'sprintf' => array( Output::i()->title, $table->page ) ) );
}
return (string) $table;
@@ -204,28 +230,28 @@ class _popular extends \IPS\Dispatcher\Controller
* 3) Fetch the search engine data and then...
* 4) Manually sort in PHP
*
* @return void
* @return string
*/
protected function leaderboard()
protected function leaderboard() : string
{
/* Figure out dates */
$dates = array();
$timezone = new \DateTimeZone( \IPS\Settings::i()->reputation_timezone );
$endDate = \IPS\DateTime::ts( time() )->setTimezone( $timezone );
$timezone = new DateTimeZone( Settings::i()->reputation_timezone );
$endDate = DateTime::ts( time() )->setTimezone( $timezone );
$firstRepDate = \IPS\Db::i()->select( 'MIN(rep_date)', 'core_reputation_index' )->first();
$firstIndexDate = \IPS\Content\Search\Index::i()->firstIndexDate();
$firstRepDate = intval( Db::i()->select( 'MIN(rep_date)', 'core_reputation_index' )->first() );
$firstIndexDate = intval( Index::i()->firstIndexDate() );
$appWhere = null;
$descApp = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_in_all_apps' );
$descApp = Member::loggedIn()->language()->addToStack( 'leaderboard_in_all_apps' );
$dates[ 'oldest' ] = \IPS\DateTime::ts( ( $firstRepDate > $firstIndexDate ) ? $firstRepDate : $firstIndexDate );
$dates[ 'oldest' ] = DateTime::ts( ( $firstRepDate > $firstIndexDate ) ? $firstRepDate : $firstIndexDate );
$oldestStamp = $dates[ 'oldest' ]->getTimeStamp();
$date = $dates[ 'oldest' ];
$aYearAgo = \IPS\DateTime::create()->setTimezone( $timezone )->sub( new \DateInterval( 'P1Y' ) );
$month = \IPS\DateTime::create()->setTimezone( $timezone )->sub( new \DateInterval( 'P1M' ) )->setTime( 0, 0 );
$week = \IPS\DateTime::create()->setTimezone( $timezone )->sub( new \DateInterval( 'P7D' ) )->setTime( 0, 0 );
$today = \IPS\DateTime::create()->setTimezone( $timezone )->setTime( 0, 0 );
$aYearAgo = DateTime::create()->setTimezone( $timezone )->sub( new DateInterval( 'P1Y' ) );
$month = DateTime::create()->setTimezone( $timezone )->sub( new DateInterval( 'P1M' ) )->setTime( 0, 0 );
$week = DateTime::create()->setTimezone( $timezone )->sub( new DateInterval( 'P7D' ) )->setTime( 0, 0 );
$today = DateTime::create()->setTimezone( $timezone )->setTime( 0, 0 );
if ( $aYearAgo->getTimeStamp() > $oldestStamp )
{
@@ -248,20 +274,20 @@ class _popular extends \IPS\Dispatcher\Controller
}
/* Got a date? */
if ( isset( \IPS\Request::i()->time ) and isset( $dates[ \IPS\Request::i()->time ] ) )
if ( isset( Request::i()->time ) and isset( $dates[ Request::i()->time ] ) )
{
$date = $dates[ \IPS\Request::i()->time ];
$date = $dates[ Request::i()->time ];
}
else if ( isset( $dates[ 'month' ] ) )
{
/* Set the default to month */
\IPS\Request::i()->time = 'month';
Request::i()->time = 'month';
$date = $dates[ 'month' ];
}
/* Applications */
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', TRUE ) as $object )
foreach ( Application::allExtensions( 'core', 'ContentRouter' ) as $object )
{
$classes = array_merge( $object->classes, $classes );
}
@@ -272,70 +298,69 @@ class _popular extends \IPS\Dispatcher\Controller
$commentClass = NULL;
$reviewClass = NULL;
if ( \IPS\IPS::classUsesTrait( $item, 'IPS\Content\Reactable' ) )
if ( IPS::classUsesTrait( $item, 'IPS\Content\Reactable' ) )
{
$areas[ $item::$application . '-' . $item::reactionType() ] = array( $item, \IPS\Member::loggedIn()->language()->addToStack( "{$item::$title}_pl" ) );
$areas[ $item::$application . '-' . $item::reactionType() ] = array( $item, Member::loggedIn()->language()->addToStack( "{$item::$title}_pl" ) );
}
if ( $item::supportsComments( \IPS\Member::loggedIn() ) and $commentClass = $item::$commentClass and \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
if ( $item::supportsComments( Member::loggedIn() ) and $commentClass = $item::$commentClass and IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
{
$commentClass = $item::$commentClass;
$supportsComments = \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) and $item::supportsComments( \IPS\Member::loggedIn() );
$supportsComments = IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) and $item::supportsComments( Member::loggedIn() );
if ( $supportsComments )
{
$areas[ $item::$application . '-' . $commentClass::reactionType() ] = array( $commentClass, \IPS\Member::loggedIn()->language()->addToStack( "{$commentClass::$title}_pl" ) );
$areas[ $item::$application . '-' . $commentClass::reactionType() ] = array( $commentClass, Member::loggedIn()->language()->addToStack( "{$commentClass::$title}_pl" ) );
}
}
if ( $item::supportsReviews( \IPS\Member::loggedIn() ) and $reviewClass = $item::$reviewClass and \IPS\IPS::classUsesTrait( $reviewClass, 'IPS\Content\Reactable' ) )
if ( $item::supportsReviews( Member::loggedIn() ) and $reviewClass = $item::$reviewClass and IPS::classUsesTrait( $reviewClass, 'IPS\Content\Reactable' ) )
{
$areas[ $item::$application . '-' . $reviewClass::reactionType() ] = array( $reviewClass, \IPS\Member::loggedIn()->language()->addToStack( "{$reviewClass::$title}_pl" ) );
$areas[ $item::$application . '-' . $reviewClass::reactionType() ] = array( $reviewClass, Member::loggedIn()->language()->addToStack( "{$reviewClass::$title}_pl" ) );
}
}
$form = new \IPS\Helpers\Form( 'popular_date', 'continue' );
$form->class = 'ipsForm_vertical';
$customStart = ( isset( \IPS\Request::i()->custom_date_start ) and \is_numeric( \IPS\Request::i()->custom_date_start ) ) ? (int) \IPS\Request::i()->custom_date_start : NULL;
$customEnd = ( isset( \IPS\Request::i()->custom_date_end ) and \is_numeric( \IPS\Request::i()->custom_date_end ) ) ? (int) \IPS\Request::i()->custom_date_end : NULL;
$form->add( new \IPS\Helpers\Form\DateRange( 'custom_date', array( 'start' => $customStart, 'end' => $customEnd ), FALSE, array( 'start' => array( 'min' => $dates[ 'oldest' ], 'time' => false ) ) ) );
$form = new Form( 'popular_date', 'continue' );
$form->class = 'ipsForm--vertical ipsForm--leaderboard';
$customStart = ( isset( Request::i()->custom_date_start ) and is_numeric( Request::i()->custom_date_start ) ) ? (int) Request::i()->custom_date_start : NULL;
$customEnd = ( isset( Request::i()->custom_date_end ) and is_numeric( Request::i()->custom_date_end ) ) ? (int) Request::i()->custom_date_end : NULL;
$form->add( new DateRange( 'custom_date', array( 'start' => $customStart, 'end' => $customEnd ), FALSE, array( 'start' => array( 'min' => $dates[ 'oldest' ], 'time' => false ) ) ) );
if ( $values = $form->values() )
{
$url = \IPS\Request::i()->url()->stripQueryString( 'time' );
$url = Request::i()->url()->stripQueryString( 'time' );
if ( isset( $values[ 'custom_date' ][ 'start' ] ) and $values[ 'custom_date' ][ 'start' ] instanceof \IPS\DateTime )
if ( isset( $values[ 'custom_date' ][ 'start' ] ) and $values[ 'custom_date' ][ 'start' ] instanceof DateTime )
{
$url = $url->setQueryString( 'custom_date_start', $values[ 'custom_date' ][ 'start' ]->getTimeStamp() );
}
if ( isset( $values[ 'custom_date' ][ 'end' ] ) and $values[ 'custom_date' ][ 'end' ] instanceof \IPS\DateTime )
if ( isset( $values[ 'custom_date' ][ 'end' ] ) and $values[ 'custom_date' ][ 'end' ] instanceof DateTime )
{
$url = $url->setQueryString( 'custom_date_end', $values[ 'custom_date' ][ 'end' ]->getTimeStamp() );
}
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
else
{
if ( $customStart )
{
$date = \IPS\DateTime::ts( $customStart )->setTimezone( $timezone )->setTime( 0, 0, 1 );
$date = DateTime::ts( $customStart )->setTimezone( $timezone )->setTime( 0, 0, 1 );
}
if ( $customEnd )
{
$endDate = \IPS\DateTime::ts( $customEnd )->setTimezone( $timezone )->setTime( 23, 59, 59 );
$endDate = DateTime::ts( $customEnd )->setTimezone( $timezone )->setTime( 23, 59, 59 );
}
}
/* Do we want results for a specific app */
$customApp = FALSE;
if ( isset( \IPS\Request::i()->in ) and isset( $areas[ \IPS\Request::i()->in ] ) )
if ( isset( Request::i()->in ) and isset( $areas[ Request::i()->in ] ) )
{
$appWhere = " AND rep_class='" . \IPS\Db::i()->escape_string( $areas[ \IPS\Request::i()->in ][ 0 ] ) . "'";
$descApp = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_in_app', FALSE, array( 'sprintf' => array( $areas[ \IPS\Request::i()->in ][ 1 ] ) ) );
$appWhere = " AND rep_class='" . Db::i()->escape_string( $areas[ Request::i()->in ][ 0 ] ) . "'";
$descApp = Member::loggedIn()->language()->addToStack( 'leaderboard_in_app', FALSE, array( 'sprintf' => array( $areas[ Request::i()->in ][ 1 ] ) ) );
$customApp = TRUE;
}
else
@@ -346,18 +371,18 @@ class _popular extends \IPS\Dispatcher\Controller
$repAreas[] = $area[0] ;
}
$appWhere = " AND " . \IPS\Db::i()->in( 'rep_class', $repAreas );
$appWhere = " AND " . Db::i()->in( 'rep_class', $repAreas );
}
$storeKey = NULL;
$hashes = NULL;
if ( ! $customStart and ! $customEnd AND ! $customApp )
{
$storeKey = 'leaderHashes_' . \IPS\Request::i()->time . '-' . md5( implode( ',', \IPS\Member::loggedIn()->groups ) );
$storeKey = 'leaderHashes_' . Request::i()->time . '-' . md5( implode( ',', Member::loggedIn()->groups ) );
if ( isset( \IPS\Data\Store::i()->$storeKey ) )
if ( isset( Store::i()->$storeKey ) )
{
$stored = \IPS\Data\Store::i()->$storeKey;
$stored = Store::i()->$storeKey;
if ( isset( $stored['hashes'] ) and isset( $stored['time'] ) and $stored['time'] > ( time() - 900 ) )
{
@@ -372,22 +397,22 @@ class _popular extends \IPS\Dispatcher\Controller
/* Prevent race condition */
if ( $storeKey )
{
\IPS\Data\Store::i()->$storeKey = array( 'time' => time(), 'hashes' => array() );
Store::i()->$storeKey = array( 'time' => time(), 'hashes' => array() );
}
/* Get rep hashes */
$inner = \IPS\Db::i()->select( 'class_type_id_hash, SUM(rep_rating) as total_rep', array( 'core_reputation_index', 'x' ), array( 'rep_date BETWEEN ' . $date->getTimeStamp() . ' AND ' . $endDate->getTimeStamp() . $appWhere ), NULL, NULL, 'class_type_id_hash' );
$repHashes = iterator_to_array( \IPS\Db::i()->select( 'class_type_id_hash, total_rep', $inner, NULL, 'x.total_rep desc', array( 0, 500 ) )->setKeyField('class_type_id_hash') );
$inner = Db::i()->select( 'class_type_id_hash, SUM(rep_rating) as total_rep', array( 'core_reputation_index', 'x' ), array( 'rep_date BETWEEN ' . $date->getTimeStamp() . ' AND ' . $endDate->getTimeStamp() . $appWhere ), NULL, NULL, 'class_type_id_hash' );
$repHashes = iterator_to_array( Db::i()->select( 'class_type_id_hash, total_rep', $inner, NULL, 'x.total_rep desc', array( 0, 500 ) )->setKeyField('class_type_id_hash') );
/* Now filter through permissions */
$searchHashes = \IPS\Content\Search\Index::i()->hashesWithPermission( array_keys( $repHashes ), \IPS\Member::loggedIn(), 500 );
$searchHashes = Index::i()->hashesWithPermission( array_keys( $repHashes ), Member::loggedIn(), 500 );
/* Filter out rep hashes not in search results */
$hashes = \array_slice( array_intersect_key( $repHashes, $searchHashes ), 0, 50 );
$hashes = array_slice( array_intersect_key( $repHashes, $searchHashes ), 0, 50 );
if ( $storeKey )
{
\IPS\Data\Store::i()->$storeKey = array( 'time' => time(), 'hashes' => $hashes );
Store::i()->$storeKey = array( 'time' => time(), 'hashes' => $hashes );
}
}
@@ -397,10 +422,10 @@ class _popular extends \IPS\Dispatcher\Controller
$results = array();
$preLoadMembers = array();
if ( \count( $hashes ) )
if ( count( $hashes ) )
{
/* Now get the reputation data */
foreach( \IPS\Db::i()->select( '*', 'core_reputation_index', array( \IPS\Db::i()->in( 'class_type_id_hash', array_keys( $hashes ) ) ) ) as $data )
foreach( Db::i()->select( '*', 'core_reputation_index', array( Db::i()->in( 'class_type_id_hash', array_keys( $hashes ) ) ) ) as $data )
{
$data['total_rep'] = $hashes[ $data['class_type_id_hash'] ]['total_rep'];
$repData[ $data['rep_class'] . '-' . $data['type_id'] ] = $data;
@@ -409,12 +434,12 @@ class _popular extends \IPS\Dispatcher\Controller
foreach( $classes as $class => $ids )
{
$or[] = \IPS\Content\Search\ContentFilter::initWithSpecificClass( $class )->onlyInIds( $ids );
$or[] = ContentFilter::initWithSpecificClass( $class )->onlyInIds( $ids );
}
/* Query and manually sort */
$sorted = array();
$search = \IPS\Content\Search\Query::init();
$search = Query::init();
/* Set the result to get as 50, as we pass in 50 unique hashes and the default is only 25, which means some may be missed as they are not sorted by rep count until after fetching */
$search->resultsToGet = 50;
@@ -432,7 +457,7 @@ class _popular extends \IPS\Dispatcher\Controller
unset( $array );
krsort( $sorted, SORT_NUMERIC );
$results = new \IPS\Content\Search\Results( $sorted, \count( $sorted ) );
$results = new Results( $sorted, count( $sorted ) );
/* Load data we need like the authors, etc */
$results->init();
@@ -442,40 +467,40 @@ class _popular extends \IPS\Dispatcher\Controller
$topContributors = array();
$innerQueryWhere = array();
$innerQueryWhere[] = array( 'member_received>0 ' . $appWhere . ' and rep_date BETWEEN ' . \intval( $date->getTimeStamp() ) . ' AND ' . \intval( $endDate->getTimeStamp() ) );
$innerQueryWhere[] = array( 'member_received>0 ' . $appWhere . ' and rep_date BETWEEN ' . $date->getTimeStamp() . ' AND ' . $endDate->getTimeStamp() );
if( \IPS\Settings::i()->leaderboard_excluded_groups )
if( Settings::i()->leaderboard_excluded_groups )
{
$innerQueryWhere[] = \IPS\Db::i()->in( 'member_group_id', explode( ',', \IPS\Settings::i()->leaderboard_excluded_groups ), TRUE );
$innerQueryWhere[] = Db::i()->in( 'member_group_id', explode( ',', Settings::i()->leaderboard_excluded_groups ), TRUE );
$innerQuery = \IPS\Db::i()->select( 'core_reputation_index.member_received as themember, SUM(rep_rating) as rep', 'core_reputation_index', $innerQueryWhere, NULL, NULL, 'themember' )->join( 'core_members', array( 'core_reputation_index.member_received = core_members.member_id' ) );
$innerQuery = Db::i()->select( 'core_reputation_index.member_received as themember, SUM(rep_rating) as rep', 'core_reputation_index', $innerQueryWhere, NULL, NULL, 'themember' )->join( 'core_members', array( 'core_reputation_index.member_received = core_members.member_id' ) );
}
else
{
$innerQuery = \IPS\Db::i()->select( 'core_reputation_index.member_received as themember, SUM(rep_rating) as rep', 'core_reputation_index', $innerQueryWhere, NULL, NULL, 'themember' );
$innerQuery = Db::i()->select( 'core_reputation_index.member_received as themember, SUM(rep_rating) as rep', 'core_reputation_index', $innerQueryWhere, NULL, NULL, 'themember' );
}
foreach( \IPS\Db::i()->select( 'themember, rep', array( $innerQuery, 'in' ), NULL, 'rep DESC', 4 )->setKeyField('themember')->setValueField('rep') as $member => $rep )
foreach( Db::i()->select( 'themember, rep', array( $innerQuery, 'in' ), NULL, 'rep DESC', 4 )->setKeyField('themember')->setValueField('rep') as $member => $rep )
{
$topContributors[ $member ] = $rep;
}
if ( \count( $topContributors ) )
if ( count( $topContributors ) )
{
$preLoadMembers = array_merge( $preLoadMembers, array_keys( $topContributors ) );
}
/* Load their data */
if ( \count( $preLoadMembers ) )
if ( count( $preLoadMembers ) )
{
foreach ( \IPS\Db::i()->select( '*', 'core_members', \IPS\Db::i()->in( 'member_id', array_unique( $preLoadMembers ) ) ) as $member )
foreach ( Db::i()->select( '*', 'core_members', Db::i()->in( 'member_id', array_unique( $preLoadMembers ) ) ) as $member )
{
\IPS\Member::constructFromData( $member );
Member::constructFromData( $member );
}
}
/* Work out the description for popular content */
if ( \IPS\Content\Reaction::isLikeMode() )
if ( Reaction::isLikeMode() )
{
$popularResultsSingle = 'popular_results_single_desc';
$popularResultsMany = 'popular_results_desc';
@@ -486,28 +511,33 @@ class _popular extends \IPS\Dispatcher\Controller
$popularResultsMany = 'popular_results_desc_rep';
}
$description = \IPS\Member::loggedIn()->language()->addToStack( ( $date->localeDate() == $endDate->localeDate() ) ? $popularResultsSingle : $popularResultsMany, NULL, array( 'sprintf' => array( $date->localeDate(), $descApp ) ) );
$description = Member::loggedIn()->language()->addToStack( ( $date->localeDate() == $endDate->localeDate() ) ? $popularResultsSingle : $popularResultsMany, NULL, array( 'sprintf' => array( $date->localeDate(), $descApp ) ) );
/* Are our offsets different? */
$tzOffsetDifference = NULL;
try
{
if ( \IPS\DateTime::ts( time() )->getOffset() != \IPS\DateTime::ts( time() )->setTimezone( $timezone )->getOffset() )
if ( DateTime::ts( time() )->getOffset() != DateTime::ts( time() )->setTimezone( $timezone )->getOffset() )
{
$tzOffsetDifference = \IPS\DateTime::ts( time() )->setTimezone( $timezone )->format('P');
$tzOffsetDifference = DateTime::ts( time() )->setTimezone( $timezone )->format('P');
}
}
catch( \Exception $ex ) { }
catch( Exception $ex ) { }
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=leaderboard", 'front', 'leaderboard_leaderboard' );
Output::i()->linkTags['canonical'] = (string) Url::internal( "app=core&module=discover&controller=popular&tab=leaderboard", 'front', 'leaderboard_leaderboard' );
/* If there are no results tell search engines not to index the page */
if( !\count( $results ) )
if( !count( $results ) )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
Output::i()->metaTags['robots'] = 'noindex';
}
/* Display */
return \IPS\Theme::i()->getTemplate('popular')->popularWrapper( $results, $areas, $topContributors, $dates, $description, $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ), $tzOffsetDifference );
return Theme::i()->getTemplate('popular')->popularWrapper( $results, $areas, $topContributors, $dates, $description, $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ), $tzOffsetDifference );
}
protected function trending(): string
{
return Bridge::i()->popularTrending();
}
}
@@ -11,68 +11,80 @@
namespace IPS\core\modules\front\discover;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\core\Rss as RssClass;
use IPS\Dispatcher\Controller;
use IPS\Login;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use OutOfRangeException;
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;
}
/**
* rss
*/
class _rss extends \IPS\Dispatcher\Controller
class rss extends Controller
{
/**
* Display Feed
*
* @return void
*/
protected function manage()
protected function manage() : void
{
try
{
$feed = \IPS\core\Rss::load( \IPS\Request::i()->id );
$feed = RssClass::load( Request::i()->id );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C340/1', 404, '' );
Output::i()->error( 'node_error', '2C340/1', 404, '' );
}
if ( !$feed->_enabled )
{
\IPS\Output::i()->error( 'node_error_no_perm', '2C340/2', 403, '' );
Output::i()->error( 'node_error_no_perm', '2C340/2', 403, '' );
}
/* Specific Member? */
if ( isset( \IPS\Request::i()->member_id ) AND isset( \IPS\Request::i()->key ) )
if ( isset( Request::i()->member_id ) AND isset( Request::i()->key ) )
{
/* Load Member */
$member = \IPS\Member::load( \IPS\Request::i()->member_id );
$member = Member::load( Request::i()->member_id );
/* Make sure we have an actual member, and that the key matches. If it doesn't, we can bubble up and see if the feed works for guests, and just use that */
if ( $member->member_id AND \IPS\Login::compareHashes( $member->getUniqueMemberHash(), (string) \IPS\Request::i()->key ) )
if ( $member->member_id AND Login::compareHashes( $member->getUniqueMemberHash(), (string) Request::i()->key ) )
{
/* Make sure we have access to this feed. */
if ( $feed->groups == '*' OR $member->inGroup( $feed->groups ) )
{
/* Send It */
\IPS\Output::i()->sendOutput( $feed->generate( $member ), 200, 'text/xml' );
Output::i()->sendOutput( $feed->generate( $member ), 200, 'text/xml' );
}
else
{
\IPS\Output::i()->error( 'node_error_no_perm', '2C340/3', 403, '' );
Output::i()->error( 'node_error_no_perm', '2C340/3', 403, '' );
}
}
}
/* We're working with a guest. */
if ( $feed->groups == '*' OR \in_array( \IPS\Settings::i()->guest_group, $feed->groups ) )
if ( $feed->groups == '*' OR in_array( Settings::i()->guest_group, $feed->groups ) )
{
\IPS\Output::i()->sendOutput( $feed->generate(), 200, 'text/xml' );
Output::i()->sendOutput( $feed->generate(), 200, 'text/xml' );
}
else
{
\IPS\Output::i()->error( 'node_error_no_perm', '2C340/4', 403, '' );
Output::i()->error( 'node_error_no_perm', '2C340/4', 403, '' );
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,350 @@
<?php
/**
* @brief tag
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 02 Apr 2024
*/
namespace IPS\core\modules\front\discover;
use IPS\Content;
use IPS\Content\Filter;
use IPS\Content\Item;
use IPS\Content\Tag as SystemTag;
use IPS\Content\Taggable;
use IPS\DateTime;
use IPS\Db;
use IPS\Helpers\CoverPhoto;
use IPS\Helpers\CoverPhoto\Controller;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Date;
use IPS\Helpers\Form\Upload;
use IPS\Helpers\Table\Content as TableContent;
use IPS\IPS;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use IPS\Theme;
use OutOfRangeException;
use function count;
use function defined;
use function sprintf;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* tag
*/
class tag extends Controller
{
/**
* @var SystemTag|null
*/
protected ?SystemTag $tag = null;
/**
* Execute
*
* @return void
*/
public function execute() : void
{
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/tags_page.css' ) );
try
{
$this->tag = SystemTag::load( Request::i()->tag, 'tag_text_seo' );
}
catch( OutOfRangeException )
{
Output::i()->error( 'node_error', '2C341/1', 404 );
}
parent::execute();
}
/**
* ...
*
* @return void
*/
protected function manage() : void
{
$contentTypes = SystemTag::getTaggableContentTypes();
$tabs = [];
foreach( $contentTypes as $type => $class )
{
$totalItems = $class::getItemsWithPermission( $this->_getWhereClause( $class ), null, null, 'read', Filter::FILTER_AUTOMATIC, 0, null, false, false, false, true );
if( $totalItems )
{
$tabs[] = $type;
}
}
if( count( $tabs ) == 1 )
{
$content = $this->_getTaggedContent( $tabs[0] );
}
elseif( isset( Request::i()->tab ) and Request::i()->tab and array_key_exists( Request::i()->tab, $contentTypes ) )
{
$content = $this->_getTaggedContent( Request::i()->tab );
}
else
{
$content = $this->_getOverview();
}
if( Request::i()->isAjax() )
{
Output::i()->sendOutput( $content );
}
Output::i()->title = $this->tag->text;
Output::i()->breadcrumb[] = [ $this->tag->url(), $this->tag->text ];
Output::i()->linkTags['canonical'] = (string) ( Request::i()->page > 1 ) ? $this->tag->url()->setPage( 'page', Request::i()->page ) : $this->tag->url() ;
Output::i()->metaTags['og:title'] = $this->tag->text;
Output::i()->metaTags['og:type'] = 'website';
Output::i()->metaTags['og:url'] = (string) $this->tag->url();
if( $image = $this->tag->coverPhotoFile() )
{
Output::i()->metaTags['og:image'] = (string) $image;
}
/* Do not set description tags for page 2+ as you end up with duplicate tags */
if ( Request::i()->page < 2 and $metaDescription = $this->tag->metaDescription() )
{
Output::i()->metaTags['description'] = $metaDescription;
Output::i()->metaTags['og:description'] = Output::i()->metaTags['description'];
}
Output::i()->output = Theme::i()->getTemplate( 'tags' )->view( $this->tag, $tabs, ( count( $tabs ) == 1 ? $content : '' ) );
}
/**
* Build the where clause to filter by content type and tag
*
* @param string $class
* @return array[]
*/
protected function _getWhereClause( string $class ) : array
{
/* @var Item $class */
$where = [
[ $class::$databasePrefix . $class::$databaseColumnId . ' IN (?)', Db::i()->select( 'tag_meta_id', 'core_tags', [ 'tag_meta_app=? and tag_meta_area=? and tag_text=?', $class::$application, $class::$module, $this->tag->text ] ) ]
];
/* Are any of these items pinned? */
if( $pinnedItems = $this->tag->getPinnedItems() )
{
$pinned = [];
$idColumn = $class::$databaseColumnId;
foreach( $pinnedItems as $pinnedItem )
{
if( $pinnedItem instanceof $class )
{
$pinned[] = $pinnedItem->$idColumn;
}
}
if( count( $pinned ) )
{
$where[] = [ Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, $pinned, true ) ];
}
}
return $where;
}
/**
* Build an overview with all available content types
*
* @return string
*/
protected function _getOverview() : string
{
$contentTypes = SystemTag::getTaggableContentTypes();
$pinnedItems = $this->tag->getPinnedItems();
$items = [];
foreach( Db::i()->select( '*', [ 'core_tags', 't' ], [
[ 't.tag_text=?', $this->tag->text ],
[ 'p.tag_perm_visible=?', 1 ],
[ '(p.tag_perm_text=? or ' . Db::i()->findInSet( 'p.tag_perm_text', Member::loggedIn()->groups ) . ')', '*' ]
], 't.tag_added desc', 50 )
->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 )
{
if( count( $items ) >= Settings::i()->tags_landing_limit )
{
break;
}
foreach( $contentTypes as $k => $itemClass )
{
if( $itemClass::$application == $row['tag_meta_app'] and $itemClass::$module == $row['tag_meta_area'] )
{
try
{
$item = $itemClass::load( $row['tag_meta_id'] );
$idColumn = $itemClass::$databaseColumnId;
$pinned = false;
if( is_array( $pinnedItems ) )
{
foreach( $pinnedItems as $pinnedItem )
{
if( get_class( $pinnedItem ) == $itemClass and $pinnedItem->$idColumn == $item->$idColumn )
{
$pinned = true;
break;
}
}
}
if( !$pinned )
{
$items[] = $item;
}
}
catch( OutOfRangeException ){}
break;
}
}
}
return Theme::i()->getTemplate( 'tags', 'core', 'front' )->overview( $items );
}
/**
* Show a table for the content type
*
* @param string $type
* @return string
*/
protected function _getTaggedContent( string $type ) : string
{
$allTypes = SystemTag::getTaggableContentTypes();
$class = $allTypes[ $type ];
$table = new TableContent( $class, $this->tag->url()->setQueryString( 'tab', $type ), $this->_getWhereClause( $class ) );
$table->classes[] = 'ipsData--grid ipsData--tags-data ipsData--tags-' . $type;
$table->limit = 20;
$table->title = sprintf( Member::loggedIn()->language()->get( 'tag_content_tagged' ), Member::loggedIn()->language()->get( $type . '_pl' ), $this->tag->text );
$table->rowsTemplate = [ Theme::i()->getTemplate( 'tags', 'core' ), 'contentTableRows' ];
$table->noModerate = true;
return (string) $table;
}
/**
* Pin an item to the top of the tag page
*
* @return void
*/
protected function pin() : void
{
if( !Member::loggedIn()->modPermission( 'can_pin_tagged' ) )
{
Output::i()->error( 'no_permission', '2C341/3', 403 );
}
$form = new Form;
$form->addMessage( 'tag_pin_info', 'ipsMessage ipsMessage--form' );
$form->add( new Date( 'tag_pin_end_date', -1, false, [ 'unlimited' => -1, 'unlimitedLang' => 'tag_pin_end_date_none' ] ) );
$form->add( new Upload( 'tag_pin_image', null, false, [
'storageExtension' => 'core_Tags',
'image' => true,
'multiple' => false
] ) );
if( $values = $form->values() )
{
$itemClass = Request::i()->itemClass;
/* what tab are we on? */
$activeTab = null;
foreach( SystemTag::getTaggableContentTypes() as $tab => $class )
{
if( $class == $itemClass )
{
$activeTab = $tab;
break;
}
}
try
{
$item = $itemClass::load( Request::i()->itemId );
$this->tag->pinItem( $item, ( $values['tag_pin_end_date'] instanceof DateTime ? $values['tag_pin_end_date'] : null ), $values['tag_pin_image'] );
}
catch( OutOfRangeException )
{
Output::i()->error( 'node_error', '2C341/2', 404 );
}
Output::i()->redirect( $this->tag->url()->setQueryString( 'tab', $activeTab ) );
}
Output::i()->output = (string) $form;
}
protected function unpin() : void
{
Request::i()->confirmedDelete();
if( !Member::loggedIn()->modPermission( 'can_pin_tagged' ) )
{
Output::i()->error( 'no_permission', '2C341/4', 403 );
}
$itemClass = Request::i()->itemClass;
try
{
$item = $itemClass::load( Request::i()->itemId );
$this->tag->removePinnedItem( $item );
}
catch( OutOfRangeException ){}
Output::i()->redirect( $this->tag->url() );
}
/**
* Get Cover Photo Storage Extension
*
* @return string
*/
protected function _coverPhotoStorageExtension(): string
{
return SystemTag::$coverPhotoStorageExtension;
}
/**
* Set Cover Photo
*
* @param CoverPhoto $photo New Photo
* @return void
*/
protected function _coverPhotoSet( CoverPhoto $photo ): void
{
$this->tag->cover_photo = (string) $photo->file;
$this->tag->cover_offset = $photo->offset;
$this->tag->save();
}
/**
* Get Cover Photo
*
* @return CoverPhoto
*/
protected function _coverPhotoGet(): CoverPhoto
{
return $this->tag->coverPhoto();
}
}
@@ -0,0 +1,60 @@
<?php
/**
* @brief Promote Items
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 1 Feb 2017
*/
namespace IPS\core\modules\front\feature;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\core\Feature\PublicTable;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Theme;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Curated "Our Picks" listing. (Very technical description there)
*/
class featured extends Controller
{
/**
* @brief These properties are used to specify datalayer context properties.
*
*/
public static array $dataLayerContext = array(
'community_area' => [ 'value' => 'our_picks', 'odkUpdate' => 'true']
);
/**
* List promoted internally promoted items
*
* @return void
*/
protected function manage() : void
{
/* Create the table */
$table = new PublicTable( Url::internal( 'app=core&module=feature&controller=featured', 'front', 'featured_show' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/promote.css' ) );
Output::i()->breadcrumb['module'] = array( Url::internal( "app=core&module=feature&controller=featured", 'front', 'featured_show' ), Member::loggedIn()->language()->addToStack('promoted_items_title') );
Output::i()->title = Member::loggedIn()->language()->addToStack('promote_table_header');
Output::i()->output = $table;
}
}
@@ -0,0 +1,56 @@
<?php
/**
* @brief languages
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 30 Apr 2024
*/
namespace IPS\core\modules\front\languages;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Http\Url;
use IPS\Lang;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* languages
*/
class languages extends \IPS\core\modules\admin\languages\languages
{
/**
* @var string[] Allowed endpoints in the parent controller
*/
protected static array $allowedEndpoints = ['addWord'];
/**
* Execute
*
* @return void
*/
public function execute() : void
{
if ( !in_array( Request::i()->do, static::$allowedEndpoints ) )
{
Output::i()->error( 'no_module_permission', '2S107/2', 403, '' );
}
parent::execute();
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+67 -90
View File
@@ -11,21 +11,35 @@
namespace IPS\core\modules\front\modcp;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\core\DataLayer;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Lang;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use IPS\Theme;
use function defined;
use function method_exists;
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;
}
/**
* Moderator Control Panel
*/
class _modcp extends \IPS\Dispatcher\Controller
class modcp extends Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
public bool $isContentPage = FALSE;
/**
* Dispatcher looks for methods to match do param, which may exist in the extension files, so this method
@@ -35,30 +49,30 @@ class _modcp extends \IPS\Dispatcher\Controller
* @param array $args Arguments
* @return void
*/
public function __call( $method, $args )
public function __call( string $method, array $args ) : void
{
if ( \IPS\Settings::i()->core_datalayer_enabled )
if ( Settings::i()->core_datalayer_enabled )
{
\IPS\core\DataLayer::i()->addContextProperty( 'community_area', \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->addToStack('modcp'), true );
DataLayer::i()->addContextProperty( 'community_area', Lang::load( Lang::defaultLanguage() )->addToStack('modcp') );
}
if ( isset( \IPS\Request::i()->do ) )
if ( isset( Request::i()->do ) )
{
$activeTab = \IPS\Request::i()->tab ?: 'overview';
foreach ( \IPS\Application::allExtensions( 'core', 'ModCp', TRUE ) as $key => $extension )
$activeTab = Request::i()->tab ?: 'overview';
foreach ( Application::allExtensions( 'core', 'ModCp' ) as $key => $extension )
{
if( method_exists( $extension, 'getTab' ) and mb_strtolower( $extension->getTab() ) == mb_strtolower( $activeTab ) )
if( mb_strtolower( $extension->getTab() ) == mb_strtolower( $activeTab ) )
{
if ( method_exists( $extension, \IPS\Request::i()->do ) )
if ( method_exists( $extension, Request::i()->do ) )
{
return $this->manage();
$this->manage();
}
}
}
}
/* Still here? */
\IPS\Output::i()->error( 'page_not_found', '2C139/5', 404, '' );
Output::i()->error( 'page_not_found', '2C139/5', 404, '' );
}
/**
@@ -66,125 +80,88 @@ class _modcp extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Check we're not a guest */
if ( !\IPS\Member::loggedIn()->member_id )
if ( !Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission', '2S194/1', 403, '' );
Output::i()->error( 'no_module_permission', '2S194/1', 403, '' );
}
/* Make sure we are a moderator */
if ( \IPS\Member::loggedIn()->modPermission() === FALSE )
if ( Member::loggedIn()->modPermission() === FALSE )
{
\IPS\Output::i()->error( 'no_module_permission', '2S194/2', 403, '' );
Output::i()->error( 'no_module_permission', '2S194/2', 403, '' );
}
/* Set up the tabs */
$activeTab = \IPS\Request::i()->tab ?: 'overview';
$activeTab = Request::i()->tab ?: 'overview';
$tabs = array( 'reports' => array(), 'approval' => array() );
$content = '';
$approvalQueueCount = 0;
foreach ( \IPS\Application::allExtensions( 'core', 'ModCp', TRUE ) as $key => $extension )
foreach ( Application::allExtensions( 'core', 'ModCp' ) as $key => $extension )
{
if( method_exists( $extension, 'getTab' ) )
/* We use this to group the sidebar for the ModCP */
$tabType = 'other';
if ( $extension->manageType() )
{
$tab = $extension->getTab();
if ( $tab )
{
$tabs[ $tab ][] = $key;
}
/* Get the count for the approval queue from the extension */
if( $tab == 'approval' )
{
$approvalQueueCount = $extension->getApprovalQueueCount( false );
}
$tabType = in_array( $extension->manageType(), array( 'content', 'members' ) ) ? $extension->manageType() : 'other';
}
if( mb_strtolower( $extension->getTab() ) == mb_strtolower( $activeTab ) )
$tab = $extension->getTab();
if ( $tab )
{
$method = ( \IPS\Request::i()->action and preg_match( '/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', \IPS\Request::i()->action ) ) ? \IPS\Request::i()->action : 'manage';
$tabs[ $tabType ][ $tab ][] = $key;
}
/* Get the count for the approval queue from the extension */
if( $tab == 'approval' )
{
$approvalQueueCount = $extension->getApprovalQueueCount( false );
}
if( mb_strtolower( (string) $extension->getTab() ) == mb_strtolower( (string) $activeTab ) )
{
$method = ( Request::i()->action and preg_match( '/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', Request::i()->action ) ) ? Request::i()->action : 'manage';
if ( $method !== 'getTab' AND ( method_exists( $extension, $method ) or method_exists( $extension, '__call' ) ) )
{
$content = $extension->$method();
if ( !$content )
{
$content = \IPS\Output::i()->output;
$content = Output::i()->output;
}
}
}
}
$tabs = array_filter( $tabs, 'count' );
/* Got a page? */
if ( !$content )
{
foreach ( $tabs as $k => $data )
foreach ( $tabs as $tabType => $tabList )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=modcp&controller=modcp&tab={$k}", 'front', "modcp_{$k}" ) );
foreach( $tabList as $k => $data )
{
Output::i()->redirect( Url::internal( "app=core&module=modcp&controller=modcp&tab={$k}", 'front', "modcp_{$k}" ) );
}
}
}
/* Content Types */
$types = $this->_getContentTypes();
/* Display */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp.css' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/modcp_responsive.css' ) );
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_modcp.js', 'core' ) );
if ( \IPS\Request::i()->isAjax() )
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/modcp.css' ) );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front_modcp.js', 'core' ) );
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->output = $content;
Output::i()->output = $content;
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'modcp' )->template( $content, $tabs, $activeTab, $types, $approvalQueueCount );
Output::i()->output = Theme::i()->getTemplate( 'modcp' )->template( $content, $tabs, $activeTab, $approvalQueueCount );
}
}
/**
* Get hidden content types
*
* @return array
*/
protected function _getContentTypes(): array
{
$types = array();
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
if ( \in_array( 'IPS\Content\Hideable', class_implements( $class ) ) )
{
if ( \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_content' ) or \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_' . $class::$title ) )
{
$types[ $class::$application . '_' . $class::$module ][ mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) ) ] = $class;
}
}
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) and $class::supportsComments( \IPS\Member::loggedIn() ) and \in_array( 'IPS\Content\Hideable', class_implements( $class::$commentClass ) ) )
{
$commentClass = $class::$commentClass;
if ( \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_content' ) or \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_' . $commentClass::$title ) )
{
$types[ $class::$application . '_' . $class::$module ][ mb_strtolower( str_replace( '\\', '_', mb_substr( $commentClass, 4 ) ) ) ] = $commentClass;
}
}
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) and $class::supportsReviews( \IPS\Member::loggedIn() ) and \in_array( 'IPS\Content\Hideable', class_implements( $class::$reviewClass ) ) )
{
$reviewClass = $class::$reviewClass;
if ( \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_content' ) or \IPS\Member::loggedIn()->modPermission( 'can_view_hidden_' . $reviewClass::$title ) )
{
$types[ $class::$application . '_' . $class::$module ][ mb_strtolower( str_replace( '\\', '_', mb_substr( $reviewClass, 4 ) ) ) ] = $reviewClass;
}
}
}
return $types;
}
}
@@ -11,22 +11,35 @@
namespace IPS\core\modules\front\online;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\core\Online\Table;
use IPS\DateTime;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Member\Group;
use IPS\Output;
use IPS\Session;
use IPS\Session\Front;
use IPS\Theme;
use function defined;
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;
}
/**
* Online Users
*/
class _online extends \IPS\Dispatcher\Controller
class online extends Controller
{
/**
* @brief These properties are used to specify datalayer context properties.
*
*/
public static $dataLayerContext = array(
public static array $dataLayerContext = array(
'community_area' => [ 'value' => 'online_user_list', 'odkUpdate' => 'true']
);
@@ -35,19 +48,19 @@ class _online extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=online&controller=online', 'front', 'online' ), array(), 'loc_viewing_online_users' );
Session::i()->setLocation( Url::internal( 'app=core&module=online&controller=online', 'front', 'online' ), array(), 'loc_viewing_online_users' );
/* Sessions are written on shutdown so let's do it now instead */
\IPS\Session\Front::i()->setTheme( \IPS\Member::loggedIn()->skin ?: 0 );
Front::i()->setTheme( Member::loggedIn()->skin ?: 0 );
session_write_close();
/* Create the table */
$table = new \IPS\core\Online\Table( \IPS\Http\Url::internal( 'app=core&module=online&controller=online', 'front', 'online' ) );
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'online', 'core', 'front' ), 'onlineUsersTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'online', 'core', 'front' ), 'onlineUsersRow' );
$table = new Table( Url::internal( 'app=core&module=online&controller=online', 'front', 'online' ) );
$table->tableTemplate = array( Theme::i()->getTemplate( 'online', 'core', 'front' ), 'onlineUsersTable' );
$table->rowsTemplate = array( Theme::i()->getTemplate( 'online', 'core', 'front' ), 'onlineUsersRow' );
$table->langPrefix = 'online_users_';
$table->include = array( 'photo', 'member_name', 'location_lang', 'running_time', 'ip_address', 'login_type' );
$table->limit = 30;
@@ -56,25 +69,25 @@ class _online extends \IPS\Dispatcher\Controller
$table->parsers = array(
'location_lang' => function( $val, $row )
{
return \IPS\Session\Front::getLocation( $row );
return Front::getLocation( $row );
},
'photo' => function( $val, $row )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userPhoto( \IPS\Member::load( $row['member_id'] ), 'mini' );
return Theme::i()->getTemplate( 'global', 'core' )->userPhoto( Member::load( $row['member_id'] ), 'fluid' );
},
'running_time' => function( $val, $row )
'running_time' => function( $val )
{
return \IPS\DateTime::ts( $val )->relative();
return DateTime::ts( $val )->relative();
},
'member_name' => function( $val, $row )
{
if( $row['member_id'] )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userLink( \IPS\Member::load( $row['member_id'] ) );
return Theme::i()->getTemplate( 'global', 'core' )->userLink( Member::load( $row['member_id'] ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'guest' );
return Member::loggedIn()->language()->addToStack( 'guest' );
}
},
);
@@ -83,28 +96,21 @@ class _online extends \IPS\Dispatcher\Controller
'filter_loggedin' => 'filter_loggedin',
);
foreach ( \IPS\Member\Group::groups( TRUE, TRUE, TRUE ) as $group )
foreach ( Group::groups( TRUE, TRUE, TRUE ) as $group )
{
/* Alias the lang keys */
$realLangKey = "core_group_{$group->g_id}";
$fakeLangKey = "online_users_group_{$group->g_id}";
\IPS\Member::loggedIn()->language()->words[ $fakeLangKey ] = \IPS\Member::loggedIn()->language()->addToStack( $realLangKey, FALSE );
if( $group->g_id == \IPS\Settings::i()->guest_group )
{
$table->filters[ 'group_' . $group->g_id ] = $group->g_id;
}
else
{
$table->filters[ 'group_' . $group->g_id ] = $group->g_id;
}
Member::loggedIn()->language()->words[ $fakeLangKey ] = Member::loggedIn()->language()->addToStack( $realLangKey, FALSE );
$table->filters[ 'group_' . $group->g_id ] = $group->g_id;
}
$table->sortDirection = $table->sortDirection ?: 'desc';
/* Display */
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( 'app=core&module=online&controller=online', 'front', 'online' );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('online_users');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'online', 'core', 'front' )->onlineUsersList( (string) $table, $table->count );
Output::i()->linkTags['canonical'] = (string) Url::internal( 'app=core&module=online&controller=online', 'front', 'online' );
Output::i()->title = Member::loggedIn()->language()->addToStack('online_users');
Output::i()->output = Theme::i()->getTemplate( 'online', 'core', 'front' )->onlineUsersList( (string) $table, $table->count );
}
}
@@ -1,61 +0,0 @@
<?php
/**
* @brief Promote Items
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 1 Feb 2017
*/
namespace IPS\core\modules\front\promote;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Curated "Our Picks" listing. (Very technical description there)
*/
class _ourpicks extends \IPS\Dispatcher\Controller
{
/**
* @brief These properties are used to specify datalayer context properties.
*
*/
public static $dataLayerContext = array(
'community_area' => [ 'value' => 'our_picks', 'odkUpdate' => 'true']
);
/**
* List promoted internally promoted items
*
* @return void
*/
protected function manage()
{
if ( ! \IPS\Settings::i()->promote_community_enabled )
{
\IPS\Output::i()->error( 'promote_no_permission', '2C356/9', 403, '' );
}
/* Create the table */
$table = new \IPS\core\Promote\PublicTable( \IPS\Http\Url::internal( 'app=core&module=promote&controller=ourpicks', 'front', 'promote_show' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote_responsive.css' ) );
}
\IPS\Output::i()->breadcrumb['module'] = array( \IPS\Http\Url::internal( "app=core&module=promote&controller=ourpicks", 'front', 'promote_show' ), \IPS\Member::loggedIn()->language()->addToStack('promoted_items_title') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_table_header');
\IPS\Output::i()->output = $table;
}
}
@@ -1,634 +0,0 @@
<?php
/**
* @brief Promote Items
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 1 Feb 2017
*/
namespace IPS\core\modules\front\promote;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Promote things. (Very technical description there)
*/
class _promote extends \IPS\Dispatcher\Controller
{
/**
* Edit internal data
*
* @return void
*/
protected function edit()
{
try
{
$promote = \IPS\core\Promote::load( \IPS\Request::i()->promote_id );
$id = $promote->class_id;
$class = $promote->class;
}
catch( \OutOfRangeException $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/5', 404, '' );
}
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'promote_manage' ) and $promote->added_by != \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission', '2C366/2', 403, '' );
}
try
{
$object = $class::load( $id );
}
catch ( \Exception $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/6', 404, '' );
}
/* Do we have viewing perms? */
if ( ! \IPS\core\Promote::objectCanView( $object ) )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/7', 404, '' );
}
$title = NULL;
$settings = $promote->form_data;
if ( ! empty( $settings['internal']['title'] ) )
{
$title = $settings['internal']['title'];
}
$form = new \IPS\Helpers\Form( 'form', 'save' );
$form->class = 'ipsForm_vertical cPromoteDialog';
$form->attributes = array( 'data-controller' => 'core.front.system.promote' );
$form->add( new \IPS\Helpers\Form\Text( 'promote_social_title_internal', $title, FALSE ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'promote_internal_text', $promote->getText('internal'), FALSE, array( 'rows' => 10 ) ) );
/* Existing media */
try
{
if ( $images = $object->contentImages( 20 ) )
{
$form->addHtml( \IPS\Theme::i()->getTemplate( 'promote' )->promoteDialogImages( $images, $promote ) );
}
}
catch( \BadMethodCallException $e ) { }
/* Upload box */
$existingMedia = array();
if ( $promote )
{
foreach( $promote->media as $file )
{
try
{
$existingMedia[] = \IPS\File::get( 'core_Promote', $file );
}
catch ( \OutOfRangeException $e ) { }
}
}
$uploader = new \IPS\Helpers\Form\Upload( 'promote_media', $existingMedia, FALSE, array(
'multiple' => TRUE,
'storageExtension' => 'core_Promote',
'maxFiles' => 10,
'image' => TRUE,
'maxFileSize' => 3,
) );
$form->add( $uploader );
/* Saving? */
if ( $values = $form->values() )
{
/* Content images */
$saveImages = array();
if ( $images )
{
foreach( $images as $image )
{
foreach( $image as $extension => $path )
{
if ( \IPS\Request::i()->attach_files and \in_array( $path, array_keys( \IPS\Request::i()->attach_files ) ) )
{
$saveImages[] = array( $extension => $path );
}
}
}
}
$media = array();
if ( \is_array( $values['promote_media'] ) )
{
foreach( $values['promote_media'] as $key => $file )
{
$media[] = (string) $file;
}
}
$settings['internal']['title'] = $values['promote_social_title_internal'];
$promote->images = $saveImages;
$promote->media = $media;
$promote->form_data = $settings;
$promote->setText( 'internal', $values['promote_internal_text'] );
$promote->save();
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'promote' ) );
/* Redirect back to the list */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promote&controller=promote&do=view', 'front', 'promote_manage' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_internal_edit');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promote' )->edit( $form );
}
/**
* View sent history
*
* @return void
*/
protected function history()
{
/* Guests can't promote things */
if( ! \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission_guest', '2C356/1', 403, '' );
}
if ( ! \IPS\core\Promote::canPromote() )
{
\IPS\Output::i()->error( 'promote_no_permission', '2C356/2', 403, '' );
}
try
{
$promote = \IPS\core\Promote::load( \IPS\Request::i()->promote_id );
}
catch( \OutOfRangeException $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/5', 404, '' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_view_history');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promote' )->history( $promote->history() );
}
/**
* List promoted items for moderators
*
* @return void
*/
protected function view()
{
/* Guests can't promote things */
if( ! \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission_guest', '2C356/1', 403, '' );
}
if ( ! \IPS\core\Promote::canPromote() )
{
\IPS\Output::i()->error( 'promote_no_permission', '2C356/2', 403, '' );
}
/* Create the table */
$table = new \IPS\core\Promote\Table( \IPS\Http\Url::internal( 'app=core&module=promote&controller=promote&do=view', 'front', 'promote_manage' ) );
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'promote_manage' ) )
{
$table->setMember( \IPS\Member::loggedIn() );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_promote.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote_responsive.css' ) );
}
\IPS\Output::i()->breadcrumb['module'] = array( \IPS\Http\Url::internal( "app=core&module=promote&controller=promote&do=view", 'front', 'promote_manage' ), \IPS\Member::loggedIn()->language()->addToStack('promote_manage_link') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_manage_link');
\IPS\Output::i()->output = $table;
}
/**
* Delete a promote item
*
* @return void
*/
protected function delete()
{
\IPS\Session::i()->csrfCheck();
if ( ! \IPS\core\Promote::canPromote() )
{
\IPS\Output::i()->error( 'no_promote_permission', '2C356/B', 403, '' );
}
try
{
$item = \IPS\core\Promote::load( \IPS\Request::i()->promote_id );
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'promote_manage' ) and $item->added_by != \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission', '2C366/1', 403, '' );
}
$item->delete();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promote&controller=promote&do=view', 'front', 'promote_manage' ), 'promote_item_deleted' );
}
catch( \OutOfRangeException $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/3', 404, '' );
}
}
/**
* Promote dialog
*
* @return void
*/
protected function manage()
{
$promote = NULL;
$existing = NULL;
/* Guests can't promote things */
if( !\IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission_guest', '2C356/4', 403, '' );
}
/* Can we promote anything at all? */
if ( ! \IPS\core\Promote::canPromote() )
{
\IPS\Output::i()->error( 'no_promote_permission', '2S356/A', 403, '' );
}
/* Editing? */
if ( isset( \IPS\Request::i()->promote_id ) )
{
try
{
$promote = \IPS\core\Promote::load( \IPS\Request::i()->promote_id );
$id = $promote->class_id;
$class = $promote->class;
if ( isset( \IPS\Request::i()->repromote ) )
{
$promote->skipCloneDuplication = TRUE;
$existing = clone $promote;
/* We're repromoting, so reset the scheduled data so we don't pre-fill an old date */
$promote->schedule_auto = 0;
$promote->scheduled = NULL;
}
}
catch( \OutOfRangeException $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/5', 404, '' );
}
}
else
{
/* Nope... */
$class = \IPS\Request::i()->class;
$id = \intval( \IPS\Request::i()->id );
/* Do we have an existing promotion waiting to be sent? */
$existing = \IPS\core\Promote::loadByClassAndId( $class, $id );
}
try
{
if( !$class OR !class_exists( $class ) OR !is_subclass_of( $class, 'IPS\Content' ) )
{
throw new \InvalidArgumentException;
}
$object = $class::load( (int) $id );
}
catch ( \Exception $ex )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/6', 404, '' );
}
/* Check that the object can be promoted */
if( !$object->canPromoteToSocialMedia() )
{
\IPS\Output::i()->error( 'page_not_found', '2C405/1', 403, 'promote_cant_promote_admin' );
}
/* Do we have viewing perms? */
if ( ! \IPS\core\Promote::objectCanView( $object ) )
{
\IPS\Output::i()->error( 'page_not_found', '2C356/7', 404, '' );
}
/* Links */
$normalLink = $object->url();
$shortLink = NULL;
$images = NULL;
try
{
$shortLink = \IPS\core\Promote::shortenUrl( $normalLink );
}
catch ( \Exception $e ) { }
/* Do we have access to any share services? */
if ( $services = \IPS\core\Promote::promoteServices() )
{
$form = new \IPS\Helpers\Form( 'form', 'promote_submit' );
$form->class = 'ipsForm_horizontal cPromoteDialog';
$form->attributes = array( 'data-controller' => 'core.front.system.promote' );
$form->addTab('promote_links');
$form->addHtml( \IPS\Theme::i()->getTemplate( 'promote' )->promoteDialogLinks( $normalLink, $shortLink ) );
$form->addTab('promote_content');
foreach( $services as $service )
{
$text = \IPS\core\Promote::objectTitle( $object );
$content = \IPS\core\Promote::objectContent( $object );
if ( $promote )
{
$service->promote = $promote;
$promoteText = $promote->text;
$text = isset( $promoteText[ mb_strtolower( $service->key ) ] ) ? $promoteText[ mb_strtolower( $service->key ) ] : '';
foreach( $service->form( $text ) as $element )
{
if ( ! ( $element instanceof \IPS\Helpers\Form\FormAbstract ) )
{
$form->addHtml( $element );
}
else
{
$form->add( $element );
}
}
}
else
{
foreach( $service->form( $text, ( $shortLink ?: $normalLink ), $content ) as $element )
{
if ( ! ( $element instanceof \IPS\Helpers\Form\FormAbstract ) )
{
$form->addHtml( $element );
}
else
{
$form->add( $element );
}
}
}
}
$form->addTab( 'promote_meta' );
/* Existing media */
try
{
if ( $images = $object->contentImages( 20 ) )
{
$form->addHtml( \IPS\Theme::i()->getTemplate( 'promote' )->promoteDialogImages( $images, $promote ) );
}
}
catch( \BadMethodCallException $e ) { }
/* Upload box */
$existingMedia = array();
if ( $promote )
{
foreach( $promote->media as $file )
{
try
{
$existingMedia[] = \IPS\File::get( 'core_Promote', $file );
}
catch ( \OutOfRangeException $e ) { }
}
}
$uploader = new \IPS\Helpers\Form\Upload( 'promote_media', $existingMedia, FALSE, array(
'multiple' => TRUE,
'storageExtension' => 'core_Promote',
'maxFiles' => 10,
'image' => TRUE,
'maxFileSize' => 3,
'template' => "promote.imageUpload",
) );
$uploader->template = array( \IPS\Theme::i()->getTemplate( 'promote', 'core', 'front' ), 'promoteAttachments' );
$form->add( $uploader );
/* Scheduling */
$form->addTab('promote_schedule');
$schedule = 'now';
if ( $promote )
{
if ( ! isset( \IPS\Request::i()->repromote ) )
{
if ( $promote->schedule_auto )
{
$schedule = 'auto';
}
else
{
$schedule = 'custom';
}
}
}
\IPS\Member::loggedIn()->language()->words['promote_custom_schedule_desc'] = sprintf( \IPS\Member::loggedIn()->language()->get('promote_custom_schedule__desc'), \IPS\Member::loggedIn()->language()->get( 'timezone__' . \IPS\Settings::i()->promote_tz ) );
/* Sort out some language strings */
if ( $promote and $promote->schedule_auto )
{
$currentSlot = \IPS\DateTime::ts( $promote->scheduled )->setTimezone( new \DateTimeZone( \IPS\Settings::i()->promote_tz ) );
/* Keep the current slot */
\IPS\Member::loggedIn()->language()->words['promote_auto_schedule_desc'] = sprintf( \IPS\Member::loggedIn()->language()->get('promote_auto_schedule__desc'), $currentSlot->html() . ' ' . $currentSlot->localeTime( FALSE ), \IPS\Settings::i()->promote_tz );
}
else
{
$nextAuto = \IPS\core\Promote::getNextAutoSchedule();
if ( $nextAuto )
{
\IPS\Member::loggedIn()->language()->words['promote_auto_schedule_desc'] = sprintf( \IPS\Member::loggedIn()->language()->get('promote_auto_schedule__desc'), $nextAuto->html() . ' ' . $nextAuto->localeTime( FALSE ), \IPS\Settings::i()->promote_tz );
}
}
$dateOptions = array(
'now' => 'promote_send_now',
'auto' => 'promote_auto_schedule',
'custom' => 'promote_custom_schedule'
);
if ( ! \IPS\Settings::i()->promote_scheduled )
{
unset( $dateOptions['auto'] );
}
$form->add( new \IPS\Helpers\Form\Radio( 'promote_schedule', $schedule, FALSE, array(
'options' => $dateOptions
) ) );
$form->add( new \IPS\Helpers\Form\Date( 'promote_custom_date', ( ( $promote and ! $promote->schedule_auto and $promote->scheduled ) ? \IPS\DateTime::ts( $promote->scheduled )->setTimezone( new \DateTimeZone( \IPS\Settings::i()->promote_tz ) ) : NULL ), FALSE, array( 'time' => true, 'timezone' => new \DateTimeZone( \IPS\Settings::i()->promote_tz ) ), NULL, NULL, NULL, 'promote_custom_date' ) );
}
else
{
\IPS\Output::i()->error( 'promote_no_permission', '2C266/1', 403, '' ); //error code needed
}
/* Saving? */
if ( $values = $form->values() )
{
$text = array();
$shareTo = array();
$processedForms = array();
foreach( $services as $service )
{
$key = mb_strtolower( $service->key );
if ( isset( $values[ 'promote_social_content_' . $key ] ) and $values[ 'promote_social_content_' . $key ] )
{
$text[ $key ] = $values[ 'promote_social_content_' . $key ];
$shareTo[] = $key;
}
$processedForms[ $key ] = $service->processPromoteForm( $values );
}
$media = array();
if ( \is_array( $values['promote_media'] ) )
{
foreach( $values['promote_media'] as $key => $file )
{
$media[] = (string) $file;
}
}
$scheduled = time();
$scheduleAuto = 0;
if ( $values['promote_schedule'] == 'custom' AND $values['promote_custom_date'] )
{
$scheduled = $values['promote_custom_date']->getTimestamp();
}
else if ( $values['promote_schedule'] == 'auto' )
{
$scheduleAuto = 1;
if ( $promote and $promote->schedule_auto )
{
$scheduled = $promote->scheduled;
}
else
{
$scheduled = \IPS\core\Promote::getNextAutoSchedule()->getTimestamp();
}
}
/* Content images */
$saveImages = array();
if ( $images )
{
foreach( $images as $image )
{
foreach( $image as $extension => $path )
{
if ( \IPS\Request::i()->attach_files and \in_array( $path, array_keys( \IPS\Request::i()->attach_files ) ) )
{
$saveImages[] = array( $extension => $path );
}
}
}
}
$isNew = FALSE;
if ( ! $promote )
{
$isNew = TRUE;
$promote = new \IPS\core\Promote;
$promote->class = $class;
$promote->class_id = $id;
$promote->added_by = \IPS\Member::loggedIn()->member_id;
}
$promote->internal = ( \in_array( 'internal', $shareTo ) ? 1 : 0 );
$promote->text = $text;
$promote->short_link = $shortLink ?: $normalLink;
$promote->images = $saveImages;
$promote->media = $media;
$promote->added = time();
$promote->scheduled = $scheduled;
$promote->schedule_auto = $scheduleAuto;
$promote->share_to = $shareTo;
$promote->failed = 0; # Reset the failed flag if we're re-promoting
$promote->sent = 0;
$promote->form_data = $processedForms;
if ( is_subclass_of( $class, 'IPS\Content' ) )
{
$promote->author_id = $object->author()->member_id ?: 0;
}
$promote->save();
if ( $isNew )
{
/* Points */
$promote->object()->author()->achievementAction( 'core', 'ContentPromotion', [
'content' => $promote->object(),
'promotype' => 'promote'
] );
}
if ( isset( \IPS\Request::i()->promote_id ) )
{
/* Redirect back to the list */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promote&controller=promote&do=view', 'front', 'promote_manage' ), 'saved' );
}
else
{
/* Show a we're done page */
$output = \IPS\Theme::i()->getTemplate( 'promote' )->promoteScheduled( $promote );
}
}
else
{
$output = \IPS\Theme::i()->getTemplate( 'promote' )->promoteDialog( \IPS\Member::loggedIn()->language()->addToStack('promote_social_title'), $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'promote', 'core' ), 'promoteDialogTemplate' ), $existing, $object ) );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_promote.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/promote_responsive.css' ) );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_social_button');
\IPS\Output::i()->output = $output;
}
}
File diff suppressed because it is too large. Load diff
@@ -11,51 +11,63 @@
namespace IPS\core\modules\front\sitemap;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Content\Controller;
use IPS\DateTime;
use IPS\Db;
use IPS\Http\Url;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use IPS\Xml\SimpleXML;
use UnderflowException;
use function defined;
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;
}
/**
* Print out sitemap
*/
class _sitemap extends \IPS\Content\Controller
class sitemap extends Controller
{
/**
* Print out the requested sitemap
*
* @return void
* @return mixed
*/
protected function manage()
protected function manage() : mixed
{
if ( isset( \IPS\Request::i()->file ) )
if ( isset( Request::i()->file ) )
{
try
{
$content = \IPS\Db::i()->select( 'data', 'core_sitemap', array( 'data IS NOT NULL AND sitemap=?', \IPS\Request::i()->file ) )->first();
$content = str_replace( '<loc>{base_url}', '<loc>' . \IPS\Settings::i()->base_url, $content );
$content = Db::i()->select( 'data', 'core_sitemap', array( 'data IS NOT NULL AND sitemap=?', Request::i()->file ) )->first();
$content = str_replace( '<loc>{base_url}', '<loc>' . Settings::i()->base_url, $content );
/* http://www.w3.org/TR/REC-xml/#charsets */
$content = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', '', $content);
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
\IPS\Output::i()->error( 'sitemap_not_found', '2C152/1', 404, '' );
Output::i()->error( 'sitemap_not_found', '2C152/1', 404, '' );
}
}
else
{
$sitemapUrl = \IPS\Http\Url::external( \IPS\Settings::i()->sitemap_url ? rtrim( \IPS\Settings::i()->sitemap_url, '/' ) : rtrim( \IPS\Settings::i()->base_url, '/' ) . '/sitemap.php' );
$sitemapUrl = Url::external( Settings::i()->sitemap_url ? rtrim( Settings::i()->sitemap_url, '/' ) : rtrim( Settings::i()->base_url, '/' ) . '/sitemap.php' );
$content = \IPS\Xml\SimpleXML::create( 'sitemapindex', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
foreach ( \IPS\Db::i()->select( array( 'sitemap', 'updated' ), 'core_sitemap', array( 'data IS NOT NULL' ) ) as $sitemap )
$content = SimpleXML::create( 'sitemapindex', 'https://www.sitemaps.org/schemas/sitemap/0.9' );
foreach ( Db::i()->select( array( 'sitemap', 'updated' ), 'core_sitemap', array( 'data IS NOT NULL' ) ) as $sitemap )
{
$content->addChild( 'sitemap', array( 'loc' => $sitemapUrl->setQueryString( 'file', $sitemap['sitemap'] ), 'lastmod' => \IPS\DateTime::ts( $sitemap['updated'] )->format('c') ) );
$content->addChild( 'sitemap', array( 'loc' => $sitemapUrl->setQueryString( 'file', $sitemap['sitemap'] ), 'lastmod' => DateTime::ts( $sitemap['updated'] )->format('c') ) );
}
$content = $content->asXML();
}
\IPS\Output::i()->sendOutput( $content, 200, 'text/xml' );
Output::i()->sendOutput( $content, 200, 'text/xml' );
}
}
@@ -11,22 +11,37 @@
namespace IPS\core\modules\front\staffdirectory;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\core\StaffDirectory\Group;
use IPS\core\StaffDirectory\User;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Editor;
use IPS\Helpers\Form\Text;
use IPS\Http\Url;
use IPS\Lang;
use IPS\Member;
use IPS\Output;
use IPS\Theme;
use OutOfRangeException;
use function defined;
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;
}
/**
* Staff directory
*/
class _directory extends \IPS\Dispatcher\Controller
class directory extends Controller
{
/**
* @brief These properties are used to specify datalayer context properties.
*/
public static $dataLayerContext = array(
public static array $dataLayerContext = array(
'community_area' => [ 'value' => 'staff', 'odkUpdate' => 'true']
);
@@ -35,9 +50,9 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function execute()
public function execute() : void
{
\IPS\Output::i()->breadcrumb['module'] = array( \IPS\Http\Url::internal( 'app=core&module=staffdirectory&controller=directory', 'front', 'staffdirectory' ), \IPS\Member::loggedIn()->language()->addToStack('staff') );
Output::i()->breadcrumb['module'] = array( Url::internal( 'app=core&module=staffdirectory&controller=directory', 'front', 'staffdirectory' ), Member::loggedIn()->language()->addToStack('staff') );
parent::execute();
}
@@ -47,29 +62,22 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$groups = \IPS\core\StaffDirectory\Group::roots();
$groups = Group::roots();
try
{
\IPS\core\StaffDirectory\User::load( \IPS\Member::loggedIn()->member_id, 'leader_type_id', array( 'leader_type=?', 'm' ) );
User::load( Member::loggedIn()->member_id, 'leader_type_id', array( 'leader_type=?', 'm' ) );
$userIsStaff = TRUE;
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$userIsStaff = FALSE;
}
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/staff.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/staff_responsive.css' ) );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('staff_directory');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'staffdirectory' )->template( $groups, $userIsStaff );
Output::i()->title = Member::loggedIn()->language()->addToStack('staff_directory');
Output::i()->output = Theme::i()->getTemplate( 'staffdirectory' )->template( $groups, $userIsStaff );
}
/**
@@ -77,21 +85,21 @@ class _directory extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function form()
protected function form() : void
{
try
{
$user = \IPS\core\StaffDirectory\User::load( \IPS\Member::loggedIn()->member_id, 'leader_type_id', array( 'leader_type=?', 'm' ) );
$user = User::load( Member::loggedIn()->member_id, 'leader_type_id', array( 'leader_type=?', 'm' ) );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C156/1', 404, '' );
Output::i()->error( 'node_error', '2C156/1', 404, '' );
}
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'leader_custom_name', ( \IPS\Member::loggedIn()->language()->checkKeyExists("core_staff_directory_name_{$user->id}") ) ? \IPS\Member::loggedIn()->language()->get("core_staff_directory_name_{$user->id}") : \IPS\Member::loggedIn()->name, FALSE ) );
$form->add( new \IPS\Helpers\Form\Text( 'leader_custom_title', ( \IPS\Member::loggedIn()->language()->checkKeyExists("core_staff_directory_title_{$user->id}") ) ? \IPS\Member::loggedIn()->language()->get("core_staff_directory_title_{$user->id}") : '', FALSE ) );
$form->add( new \IPS\Helpers\Form\Editor( 'leader_custom_bio', \IPS\Member::loggedIn()->language()->addToStack("core_staff_directory_bio_{$user->id}"), FALSE, array(
$form = new Form;
$form->add( new Text( 'leader_custom_name', ( Member::loggedIn()->language()->checkKeyExists("core_staff_directory_name_{$user->id}") ) ? Member::loggedIn()->language()->get("core_staff_directory_name_{$user->id}") : Member::loggedIn()->name, FALSE ) );
$form->add( new Text( 'leader_custom_title', ( Member::loggedIn()->language()->checkKeyExists("core_staff_directory_title_{$user->id}") ) ? Member::loggedIn()->language()->get("core_staff_directory_title_{$user->id}") : '', FALSE ) );
$form->add( new Editor( 'leader_custom_bio', Member::loggedIn()->language()->addToStack("core_staff_directory_bio_{$user->id}"), FALSE, array(
'app' => 'core',
'key' => 'Staffdirectory',
'autoSaveKey' => 'leader-' . $user->id,
@@ -101,17 +109,17 @@ class _directory extends \IPS\Dispatcher\Controller
if( $values = $form->values() )
{
/* Save */
\IPS\Lang::saveCustom( 'core', "core_staff_directory_name_{$user->id}", $values['leader_custom_name'] );
\IPS\Lang::saveCustom( 'core', "core_staff_directory_title_{$user->id}", $values['leader_custom_title'] );
\IPS\Lang::saveCustom( 'core', "core_staff_directory_bio_{$user->id}", $values['leader_custom_bio'] );
Lang::saveCustom( 'core', "core_staff_directory_name_{$user->id}", $values['leader_custom_name'] );
Lang::saveCustom( 'core', "core_staff_directory_title_{$user->id}", $values['leader_custom_title'] );
Lang::saveCustom( 'core', "core_staff_directory_bio_{$user->id}", $values['leader_custom_bio'] );
//$user->save();
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=staffdirectory&controller=directory", 'front', 'staffdirectory' ), 'saved' );
Output::i()->redirect( Url::internal( "app=core&module=staffdirectory&controller=directory", 'front', 'staffdirectory' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('leader_edit_mine');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->genericBlock( $form, '', 'ipsPad' );
Output::i()->title = Member::loggedIn()->language()->addToStack('leader_edit_mine');
Output::i()->output = Theme::i()->getTemplate( 'global' )->genericBlock( $form, '', 'i-padding_3' );
}
}
@@ -1,79 +0,0 @@
<?php
/**
* @brief Status Updates Feed
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 15 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\core\modules\front\status;
/* To prevent PHP errors (extending class does not exist) revealing path */
if (!\defined('\IPS\SUITE_UNIQUE_KEY')) {
header((isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 403 Forbidden');
exit;
}
/**
* Status Updates
*/
class _ajaxcreate extends \IPS\Dispatcher\Controller
{
/**
* Status Update Create Form
*
* @return void
*/
protected function manage()
{
if ( !\IPS\Settings::i()->profile_comments )
{
\IPS\Output::i()->error('node_error', '2C231/2', 403, '');
}
if ( \IPS\Member::loggedIn()->member_id and !\IPS\Member::loggedIn()->pp_setting_count_comments )
{
\IPS\Output::i()->redirect( \IPS\Member::loggedIn()->url()->setQueryString( 'tab', 'statuses' ) );
}
if ( !\IPS\core\Statuses\Status::canCreateFromCreateMenu() )
{
\IPS\Output::i()->error('no_module_permission', '2C231/1', 403, '');
}
$form = new \IPS\Helpers\Form( 'new_status', 'status_new' );
foreach ( \IPS\core\Statuses\Status::formElements( NULL, NULL, TRUE ) AS $k => $element )
{
$form->add( $element );
}
if ( $values = $form->values() )
{
$values['status_content'] = $values['status_content_ajax'];
$status = \IPS\core\Statuses\Status::createFromForm( $values );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array(
'redirect' => (string) \IPS\Member::loggedIn()->url(),
'message' => '',
'content' => \IPS\Theme::i()->getTemplate( 'widgets' )->recentStatusUpdatesStatus( $status )
) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Member::loggedIn()->url(), NULL, 303 );
}
}
$form = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'statusPopupTemplate' ) );
if ( \IPS\core\Statuses\Status::moderateNewItems( \IPS\Member::loggedIn() ) )
{
$form = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->postingInformation( NULL, TRUE, FALSE ) . $form;
}
\IPS\Output::i()->output = $form;
}
}
File diff suppressed because it is too large. Load diff
@@ -14,24 +14,32 @@ namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\core\Alerts\Alert;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use OutOfRangeException;
use function defined;
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' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* alerts
*/
class _alerts extends \IPS\Dispatcher\Controller
class alerts extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
parent::execute();
}
@@ -41,26 +49,31 @@ class _alerts extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function dismiss()
protected function dismiss() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Update user last seen */
try
{
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
$alert = Alert::load( Request::i()->id );
if( $alert->reply == Alert::REPLY_REQUIRED and \IPS\Member::loggedIn()->member_id and \IPS\Member::loggedIn()->canUseMessenger() and \IPS\Member::load( $alert->member_id )->member_id )
if( $alert->reply == 2 and Member::loggedIn()->member_id and Member::loggedIn()->canUseMessenger() )
{
\IPS\Output::i()->error( 'alert_cant_dismiss', '3C428/1', 403, '' );
Output::i()->error( 'alert_cant_dismiss', '3C428/1', 403, '' );
}
$alert->dismiss();
}
catch( \OutOfRangeException $e ) {}
catch( OutOfRangeException $e ) {}
if ( Request::i()->isAjax() )
{
Output::i()->json( [ 'message' => 'ok' ] );
}
/* Redirect */
\IPS\Output::i()->redirect( base64_decode( \IPS\Request::i()->ref ) );
Output::i()->redirect( base64_decode( Request::i()->ref ?: '' ) );
}
/**
@@ -68,10 +81,10 @@ class _alerts extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function viewReplies()
protected function viewReplies() : void
{
\IPS\core\Alerts\Alert::setAlertCurrentlyFilteringMessages( \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id ) );
Alert::setAlertCurrentlyFilteringMessages( Alert::load( Request::i()->id ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=messaging&controller=messenger&overview=1') );
Output::i()->redirect( Url::internal('app=core&module=messaging&controller=messenger&overview=1') );
}
}
@@ -11,68 +11,85 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Content\Controller;
use IPS\core\Announcements\Announcement as AnnouncementClass;
use IPS\Http\Url;
use IPS\Http\Url\Exception;
use IPS\Http\Url\Friendly;
use IPS\Http\Url\Internal;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use OutOfRangeException;
use function defined;
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;
}
/**
* Announcement
*/
class _announcement extends \IPS\Content\Controller
class announcement extends Controller
{
/**
* [Content\Controller] Class
*/
protected static $contentModel = 'IPS\core\Announcements\Announcement';
protected static string $contentModel = 'IPS\core\Announcements\Announcement';
/**
* View Announcement
*
* @return void
* @return mixed
*/
protected function manage()
protected function manage() : mixed
{
parent::manage();
/* Load announcement */
try
{
$announcement = \IPS\core\Announcements\Announcement::load( \IPS\Request::i()->id );
$announcement = AnnouncementClass::load( Request::i()->id );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'announcement_missing', '2C199/1', 404, '' );
Output::i()->error( 'announcement_missing', '2C199/1', 404, '' );
}
if ( !$announcement->canView() )
{
\IPS\Output::i()->error( 'node_error_no_perm', '2C199/2', 403, '' );
Output::i()->error( 'node_error_no_perm', '2C199/2', 403, '' );
}
/* Display */
$announcementHtml = \IPS\Theme::i()->getTemplate( 'system' )->announcement( $announcement );
if ( \IPS\Request::i()->isAjax() )
$announcementHtml = Theme::i()->getTemplate( 'system' )->announcement( $announcement );
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( $announcementHtml );
Output::i()->sendOutput( $announcementHtml );
}
else
{
/* if the site is offline, use the minimal layout */
if ( ( !\IPS\Settings::i()->site_online and !\IPS\Member::loggedIn()->group['g_access_offline'] ) OR ( !\IPS\Member::loggedIn()->member_id AND !\IPS\Member::loggedIn()->group['g_view_board'] ) )
if ( ( !Settings::i()->site_online and !Member::loggedIn()->group['g_access_offline'] ) OR ( !Member::loggedIn()->member_id AND !Member::loggedIn()->group['g_view_board'] ) )
{
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->allowDefaultWidgets = FALSE;
\IPS\Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->allowDefaultWidgets = FALSE;
Output::i()->sidebar['enabled'] = FALSE;
}
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=announcement&id=' . $announcement->id, NULL, 'announcement', $announcement->seo_title ), array(), 'loc_viewing_announcement', array( $announcement->title => FALSE ) );
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=announcement&id=' . $announcement->id, NULL, 'announcement', $announcement->seo_title ), array(), 'loc_viewing_announcement', array( $announcement->title => FALSE ) );
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( $announcement->title );
\IPS\Output::i()->output = $announcementHtml;
Output::i()->title = Member::loggedIn()->language()->addToStack( $announcement->title );
Output::i()->output = $announcementHtml;
}
return null;
}
/**
@@ -80,35 +97,35 @@ class _announcement extends \IPS\Content\Controller
*
* @return void
*/
protected function permissionCheck()
protected function permissionCheck() : void
{
if ( !\IPS\Member::loggedIn()->modPermission( 'can_manage_announcements' ) )
if ( !Member::loggedIn()->modPermission( 'can_manage_announcements' ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C185/1', 403, '' );
Output::i()->error( 'no_module_permission', '2C185/1', 403, '' );
}
try
{
$url = \IPS\Http\Url::createFromString( \IPS\Request::i()->url, TRUE );
$url = Url::createFromString( Request::i()->url );
}
catch( \IPS\Http\Url\Exception $e )
catch( Exception $e )
{
\IPS\Output::i()->json( array( 'status' => 'unexpected_format' ) );
Output::i()->json( array( 'status' => 'unexpected_format' ) );
}
/* Make sure this is a local URL */
if( !( $url instanceof \IPS\Http\Url\Internal ) )
if( !( $url instanceof Internal ) )
{
\IPS\Output::i()->json( array( 'status' => 'not_local' ) );
Output::i()->json( array( 'status' => 'not_local' ) );
}
/* Get the definition */
$furlDefinition = \IPS\Http\Url\Friendly::furlDefinition();
$furlDefinition = Friendly::furlDefinition();
/* If we don't have a validate callback, we can return NULL */
if ( !isset( $furlDefinition[ $url->seoTemplate ]['verify'] ) or !$furlDefinition[ $url->seoTemplate ]['verify'] )
{
\IPS\Output::i()->json( array( 'status' => 'not_verified' ) );
Output::i()->json( array( 'status' => 'not_verified' ) );
}
$class = $furlDefinition[ $url->seoTemplate ]['verify'];
@@ -117,7 +134,7 @@ class _announcement extends \IPS\Content\Controller
/* If the class does not have our method, return a not_verified status */
if( !method_exists( $item, 'cannotViewGroups' ) )
{
\IPS\Output::i()->json( array( 'status' => 'not_verified' ) );
Output::i()->json( array( 'status' => 'not_verified' ) );
}
/* Get groups that cannot view the item */
@@ -125,9 +142,9 @@ class _announcement extends \IPS\Content\Controller
if( !$groups )
{
\IPS\Output::i()->json( array( 'status' => 'all_permissions' ) );
Output::i()->json( array( 'status' => 'all_permissions' ) );
}
\IPS\Output::i()->json( array( 'html' => \IPS\Theme::i()->getTemplate( 'modcp', 'core', 'front' )->announcementGroupCheck( $groups ) ) );
Output::i()->json( array( 'html' => Theme::i()->getTemplate( 'modcp', 'core', 'front' )->announcementGroupCheck( $groups ) ) );
}
}
@@ -11,34 +11,50 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\core\Attachments\Table;
use IPS\core\extensions\core\EditorMedia\Attachment;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use OutOfRangeException;
use UnderflowException;
use function defined;
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;
}
/**
* My Attachments Controller
*/
class _attachments extends \IPS\Dispatcher\Controller
class attachments extends Controller
{
/**
* Manage
*
* @return void
*/
public function manage()
public function manage() : void
{
/* Logged in and can upload only */
if ( !\IPS\Member::loggedIn()->member_id or \IPS\Member::loggedIn()->group['g_attach_max'] == 0 )
if ( !Member::loggedIn()->member_id or Member::loggedIn()->group['g_attach_max'] == 0 )
{
\IPS\Output::i()->error( 'no_module_permission', '2C229/1', 403, '' );
Output::i()->error( 'no_module_permission', '2C229/1', 403, '' );
}
/* Build Table */
$table = new \IPS\core\Attachments\Table( 'core_attachments', \IPS\Http\Url::internal( 'app=core&module=system&controller=attachments', 'front', 'attachments' ), array( array( 'attach_member_id=?', \IPS\Member::loggedIn()->member_id ) ) );
$table = new Table( 'core_attachments', Url::internal( 'app=core&module=system&controller=attachments', 'front', 'attachments' ), array( array( 'attach_member_id=?', Member::loggedIn()->member_id ) ) );
$table->include = array( 'attach_id', 'attach_location', 'attach_date', 'attach_file', 'attach_filesize', 'attach_is_image', 'attach_content', 'attach_hits', 'attach_security_key' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate('myAttachments'), 'rows' );
$table->rowsTemplate = array( Theme::i()->getTemplate('myAttachments'), 'rows' );
$table->joins = array();
/* Sort */
@@ -54,13 +70,13 @@ class _attachments extends \IPS\Dispatcher\Controller
$self = $this;
$table->parsers = array( 'attach_content' => function( $val, $row ) use ( $self )
{
return \IPS\core\extensions\core\EditorMedia\Attachment::getLocations( $row['attach_id'] );
return Attachment::getLocations( $row['attach_id'] );
} );
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('my_attachments');
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('my_attachments') );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('myAttachments')->template( (string) $table, \IPS\Db::i()->select( 'SUM(attach_filesize)', 'core_attachments', array( 'attach_member_id=?', \IPS\Member::loggedIn()->member_id ) )->first(), \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments', array( 'attach_member_id=?', \IPS\Member::loggedIn()->member_id ) )->first() );
Output::i()->title = Member::loggedIn()->language()->addToStack('my_attachments');
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('my_attachments') );
Output::i()->output = Theme::i()->getTemplate('myAttachments')->template( (string) $table, Db::i()->select( 'SUM(attach_filesize)', 'core_attachments', array( 'attach_member_id=?', Member::loggedIn()->member_id ) )->first(), Db::i()->select( 'COUNT(*)', 'core_attachments', array( 'attach_member_id=?', Member::loggedIn()->member_id ) )->first() );
}
/**
@@ -68,50 +84,50 @@ class _attachments extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function rotate()
protected function rotate() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Get attachment to make sure it's valid */
try
{
$attachment = \IPS\Db::i()->select( '*', 'core_attachments', [ 'attach_id=? and attach_is_image=?', \IPS\Request::i()->id, 1 ] )
$attachment = Db::i()->select( '*', 'core_attachments', [ 'attach_id=? and attach_is_image=?', Request::i()->id, 1 ] )
->join( 'core_attachments_map', 'core_attachments.attach_id=core_attachments_map.attachment_id' )
->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Output::i()->json( array( 'error' => 'node_error' ) );
Output::i()->json( array( 'error' => 'node_error' ) );
}
/* Check Permission */
$exploded = explode( '_', $attachment['location_key'] );
try
{
$extensions = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'EditorLocations' );
$extensions = Application::load( $exploded[0] )->extensions( 'core', 'EditorLocations' );
if ( isset( $extensions[ $exploded[1] ] ) )
{
$attachmentItem = $extensions[ $exploded[1] ]->attachmentLookup( $attachment[ 'id1' ], $attachment[ 'id2' ], $attachment[ 'id3' ] );
}
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->json( array( 'error' => 'no_permission' ) );
Output::i()->json( array( 'error' => 'no_permission' ) );
}
/* If we have permission to edit the post, we're going to save the rotation angle */
if( $attachmentItem->author()->member_id == \IPS\Member::loggedIn()->member_id OR $attachmentItem->canEdit() )
if( $attachmentItem->author()->member_id == Member::loggedIn()->member_id OR $attachmentItem->canEdit() )
{
$baseRotation = $attachment['attach_img_rotate'];
$temp = false;
}
else
{
$baseRotation = \IPS\Request::i()->current ?? 0;
$baseRotation = Request::i()->current ?? 0;
$temp = true;
}
$angle = \IPS\Request::i()->direction == 'right' ? 90 : -90;
$angle = Request::i()->direction == 'right' ? 90 : -90;
$rotation = $baseRotation + $angle;
/* Make sure we don't rotate to some stupid number */
@@ -127,21 +143,21 @@ class _attachments extends \IPS\Dispatcher\Controller
/* Store the angle for the future */
if( !$temp )
{
\IPS\Db::i()->update( 'core_attachments', [ 'attach_img_rotate' => $rotation ], [ 'attach_id=?', $attachment[ 'attach_id' ] ] );
Db::i()->update( 'core_attachments', [ 'attach_img_rotate' => $rotation ], [ 'attach_id=?', $attachment[ 'attach_id' ] ] );
}
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
\IPS\Output::i()->json( [
Output::i()->json( [
'rotate' => $rotation,
'message' => \IPS\Member::loggedIn()->language()->addToStack('gallery_image_rotated'),
'fileId' => \IPS\Request::i()->id,
'message' => Member::loggedIn()->language()->addToStack('gallery_image_rotated'),
'fileId' => Request::i()->id,
'saved' => $temp ? 0 : 1
] );
}
else
{
\IPS\Output::i()->redirect( $attachmentItem->url() );
Output::i()->redirect( $attachmentItem->url() );
}
}
}
@@ -11,39 +11,47 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Output;
use IPS\Request;
use OutOfRangeException;
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;
}
/**
* "Content" functions Controller
*/
class _content extends \IPS\Dispatcher\Controller
class content extends Controller
{
/**
* Find content
*
* @return void
*/
protected function find()
protected function find() : void
{
if ( ! \IPS\Request::i()->content_class AND ! \IPS\Request::i()->content_id AND ! \IPS\Request::i()->content_commentid )
if ( ! Request::i()->content_class AND ! Request::i()->content_id AND ! Request::i()->content_commentid )
{
\IPS\Output::i()->error( 'node_error', '2S226/1', 404, '' );
Output::i()->error( 'node_error', '2S226/1', 404, '' );
}
$class = 'IPS\\' . implode( '\\', explode( '_', \IPS\Request::i()->content_class ) );
$class = 'IPS\\' . implode( '\\', explode( '_', Request::i()->content_class ) );
if ( ! class_exists( $class ) or ! \in_array( 'IPS\Content', class_parents( $class ) ) )
if ( ! class_exists( $class ) or ! in_array( 'IPS\Content', class_parents( $class ) ) )
{
\IPS\Output::i()->error( 'node_error', '2S226/2', 404, '' );
Output::i()->error( 'node_error', '2S226/2', 404, '' );
}
try
{
$item = $class::load( \IPS\Request::i()->content_id );
$item = $class::load( Request::i()->content_id );
if( isset( $item::$archiveClass ) AND method_exists( $item, 'isArchived' ) AND $item->isArchived() )
{
@@ -54,21 +62,21 @@ class _content extends \IPS\Dispatcher\Controller
$commentClass = $class::$commentClass;
}
$comment = $commentClass::load( \IPS\Request::i()->content_commentid );
$comment = $commentClass::load( Request::i()->content_commentid );
}
catch( \OutOfRangeException $ex )
catch( OutOfRangeException $ex )
{
\IPS\Output::i()->error( 'node_error', '2S226/3', 404, '' );
Output::i()->error( 'node_error', '2S226/3', 404, '' );
}
/* Make sure we have permission to see this */
if ( $item->canView() AND $comment->canView() )
{
\IPS\Output::i()->redirect( $comment->url() );
Output::i()->redirect( $comment->url() );
}
else
{
\IPS\Output::i()->error( 'node_error', '2S226/4', 404, '' );
Output::i()->error( 'node_error', '2S226/4', 404, '' );
}
}
}
@@ -10,27 +10,34 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Helpers\Form\YesNo;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Http\Url\Exception;
use IPS\Http\Url\Internal;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use function defined;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/* To prevent PHP errors (extending class does not exist) revealing path */
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;
}
/**
* Cookie Information Page
*/
class _cookies extends \IPS\Dispatcher\Controller
class cookies extends Controller
{
public function execute()
public function execute(): void
{
parent::execute();
\IPS\Output::setCacheTime( false );
Output::i()->pageCaching = FALSE;
}
/**
@@ -38,17 +45,18 @@ class _cookies extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage(): void
{
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=cookies', NULL, 'cookies' ), array(), 'loc_viewing_cookie_policy' );
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('cookies_about') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cookies_about');
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->cookies();
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=cookies', NULL, 'cookies' ), array(), 'loc_viewing_cookie_policy' );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('cookies_about') );
Output::i()->title = Member::loggedIn()->language()->addToStack('cookies_about');
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->metaTags['robots'] = 'noindex';
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->cookies();
}
/**
@@ -56,26 +64,26 @@ class _cookies extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function cookieConsentToggle()
protected function cookieConsentToggle(): void
{
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->setAllowOptionalCookies( (bool) \IPS\Request::i()->status );
if ( isset( \IPS\Request::i()->ref ) )
Session::i()->csrfCheck();
Member::loggedIn()->setAllowOptionalCookies( (bool) Request::i()->status );
if ( isset( Request::i()->ref ) )
{
try
{
$url = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
$url = Url::createFromString( base64_decode( Request::i()->ref ) );
}
catch( \IPS\Http\Url\Exception $e )
catch( Exception $e )
{
$url = NULL;
}
if ( $url instanceof \IPS\Http\Url\Internal and !$url->openRedirect() )
if ( $url instanceof Internal and !$url->openRedirect() )
{
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal(''));
Output::i()->redirect( Url::internal('') );
}
}
@@ -11,24 +11,32 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use function defined;
use function intval;
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;
}
/**
* Redirect
*/
class _designermode extends \IPS\Dispatcher\Controller
class designermode extends Controller
{
/**
* Something is wrong
*
* @return void
*/
protected function missing()
protected function missing() : void
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'theme_designers_mode_error_missing', FALSE, array( 'sprintf' => array( \intval( \IPS\Request::i()->id ) ) ) ), "4C370/1", 500 );
Output::i()->error( Member::loggedIn()->language()->addToStack( 'theme_designers_mode_error_missing', FALSE, array( 'sprintf' => array( intval( Request::i()->id ) ) ) ), "4C370/1" );
}
}
@@ -1,65 +0,0 @@
<?php
/**
* @brief Easy mode controller
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 15 Oct 2021
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Redirect
*/
class _easymode extends \IPS\Dispatcher\Controller
{
/**
* Proxy remote CSS URLs to allow for JS to select classes
*
* @return void
*/
protected function proxy()
{
if ( \IPS\Theme::isUsingEasyModeEditor() )
{
if ( isset( \IPS\Request::i()->f ) )
{
$url = base64_decode( \IPS\Request::i()->f );
$storage = \IPS\File::getClass( 'core_Theme' );
$storageUrl = parse_url( $storage->baseUrl() );
$test = parse_url( $url );
/* Does this URL match the host of the storage URL? */
if ( preg_match( "/\.css(\.gz)?(\?|$)/", $url ) and isset( $test['host'] ) and $test['host'] == $storageUrl['host'] )
{
$encoded = ( preg_match( "/\.css\.gz?(\?|$)/", $url ) );
try
{
$css = \IPS\Http\Url::external( $url )->setScheme('https')->request()->get();
if ( $encoded )
{
$css = gzdecode( $css );
}
\IPS\Output::i()->sendOutput( $css, 200, 'text/css' );
}
catch( \Exception $e ) { }
}
}
}
/* still here? */
\IPS\Output::i()->sendOutput( '/* Could not read ' . $url . '*/', 200, 'text/css' );
}
}
+416 -264
View File
@@ -11,101 +11,104 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use BadMethodCallException;
use DateInterval;
use DomainException;
use Exception;
use IPS\Application;
use IPS\core\StoredReplies;
use IPS\Data\Cache;
use IPS\DateTime;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\Http\Url;
use IPS\IPS;
use IPS\Helpers\Form\Editor as FormEditor;
use IPS\Log;
use IPS\Member;
use IPS\Output;
use IPS\Redis;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Text\Parser;
use IPS\Theme;
use OutOfRangeException;
use UnexpectedValueException;
use function class_exists;
use function count;
use function defined;
use function in_array;
use function intval;
use function md5;
use function strtolower;
use function strtoupper;
use function substr;
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;
}
/**
* Editor AJAX functions Controller
*/
class _editor extends \IPS\Dispatcher\Controller
class editor extends Controller
{
/**
* Preview iframe
*
* @return void
*/
protected function preview()
{
$output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->preview( \IPS\Request::i()->editor_id );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $output ) );
}
/**
* Link Dialog
*
* @return void
*/
protected function link()
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->link( \IPS\Request::i()->current, \IPS\Request::i()->editorId, isset( \IPS\Request::i()->block ) );
}
/**
* Code Dialog
*
* @return void
*/
protected function code()
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->code( htmlentities( \IPS\Request::i()->val, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8' ), \IPS\Request::i()->editorId, md5( mt_rand() ), \IPS\Request::i()->lang ?: '' );
}
/**
* Image Dialog
*
* @return void
*/
protected function image()
protected function image() : void
{
$maxImageDims = \IPS\Settings::i()->attachment_image_size ? explode( 'x', \IPS\Settings::i()->attachment_image_size ) : array( 1000, 750 );
$maxImageDims = Settings::i()->attachment_image_size ? explode( 'x', Settings::i()->attachment_image_size ) : array( 1000, 750 );
/* Let's do some casting here */
\IPS\Request::i()->width = (int) \IPS\Request::i()->width;
\IPS\Request::i()->height = (int) \IPS\Request::i()->height;
\IPS\Request::i()->actualWidth = (int) \IPS\Request::i()->actualWidth;
\IPS\Request::i()->actualHeight = (int) \IPS\Request::i()->actualHeight;
Request::i()->width = (int) Request::i()->width;
Request::i()->height = (int) Request::i()->height;
Request::i()->actualWidth = (int) Request::i()->actualWidth;
Request::i()->actualHeight = (int) Request::i()->actualHeight;
$maxImageDims = array_map('intval', $maxImageDims);
foreach( array( 'width', 'height', 'actualWidth', 'actualHeight' ) as $key )
{
if( \IPS\Request::i()->$key <= 0 )
if( Request::i()->$key <= 0 )
{
\IPS\Output::i()->error( 'invalid_image_dimensions', '2C270/2', 403, '' );
Output::i()->error( 'invalid_image_dimensions', '2C270/2', 403, '' );
}
}
$ratioH = round( \IPS\Request::i()->height / \IPS\Request::i()->width, 2 );
$ratioW = round( \IPS\Request::i()->width / \IPS\Request::i()->height, 2 );
if( $maxImageDims[0] === 0 && $maxImageDims[1] === 0 )
$ratioH = round( Request::i()->height / Request::i()->width, 2 );
$ratioW = round( Request::i()->width / Request::i()->height, 2 );
if( $maxImageDims[0] === 0 && $maxImageDims[1] === 0 )
{
$maxWidth = (int) \IPS\Request::i()->actualWidth;
$maxHeight = (int) \IPS\Request::i()->actualHeight;
$maxWidth = (int) Request::i()->actualWidth;
$maxHeight = (int) Request::i()->actualHeight;
}
else
{
$maxWidth = ( \IPS\Request::i()->actualWidth < $maxImageDims[0] ) ? \IPS\Request::i()->actualWidth : $maxImageDims[0];
$maxHeight = ( \IPS\Request::i()->actualHeight < $maxImageDims[1] ) ? \IPS\Request::i()->actualHeight : $maxImageDims[1];
$maxWidth = ( Request::i()->actualWidth < $maxImageDims[0] ) ? Request::i()->actualWidth : $maxImageDims[0];
$maxHeight = ( Request::i()->actualHeight < $maxImageDims[1] ) ? Request::i()->actualHeight : $maxImageDims[1];
}
if ( \IPS\Request::i()->width > $maxWidth )
if ( Request::i()->width > $maxWidth )
{
\IPS\Request::i()->width = $maxWidth;
\IPS\Request::i()->height = floor( \IPS\Request::i()->width * $ratioH );
Request::i()->width = $maxWidth;
Request::i()->height = floor( Request::i()->width * $ratioH );
}
if ( \IPS\Request::i()->height > $maxHeight )
if ( Request::i()->height > $maxHeight )
{
\IPS\Request::i()->height = $maxHeight;
\IPS\Request::i()->width = floor( \IPS\Request::i()->height * $ratioW );
Request::i()->height = $maxHeight;
Request::i()->width = floor( Request::i()->height * $ratioW );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->image( \IPS\Request::i()->editorId, \IPS\Request::i()->width, \IPS\Request::i()->height, $maxWidth, $maxHeight, \IPS\Request::i()->float, \IPS\Request::i()->link, $ratioW, $ratioH, urldecode( \IPS\Request::i()->imageAlt ), \IPS\Request::i()->editorUniqueId );
Output::i()->output = Theme::i()->getTemplate( 'editor', 'core', 'global' )->image( Request::i()->editorId, Request::i()->width, Request::i()->height, $maxWidth, $maxHeight, Request::i()->float, Request::i()->link, $ratioW, $ratioH, urldecode( Request::i()->imageAlt ), Request::i()->editorUniqueId );
}
/**
@@ -113,146 +116,254 @@ class _editor extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function validateLink()
protected function validateLink() : void
{
/* CSRF check */
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
$internalEmbedAllowed = FormEditor::memberHasPermission( 'internal_embed', comments: isset( Request::i()->commenteditor ) );
$externalEmbedAllowed = !Request::i()->noEmbed and FormEditor::memberHasPermission( 'external_embed', comments: isset( Request::i()->commenteditor ) );
$imageRequest = boolval( !Request::i()->noEmbed and Request::i()->image and Request::i()->width and Request::i()->height );
$externalImageAllowed = $imageRequest and $externalEmbedAllowed and FormEditor::memberHasPermission( 'external_image', comments: isset( Request::i()->commenteditor ) );
/* Have we recently checked and validated this link? */
$cacheKey = md5( \IPS\Request::i()->url . (bool) \IPS\Request::i()->noEmbed . (bool) \IPS\Request::i()->image . \IPS\Member::loggedIn()->language()->id );
$cacheKey = 'url_validate_' . md5( json_encode([
Request::i()->url,
(bool) Request::i()->noEmbed,
Member::loggedIn()->language()->id,
$imageRequest,
$externalEmbedAllowed,
$externalImageAllowed,
$internalEmbedAllowed,
]));
try
{
$cachedResult = \IPS\Data\Cache::i()->getWithExpire( $cacheKey, TRUE );
\IPS\Output::i()->json( $cachedResult );
$cachedResult = Cache::i()->getWithExpire( $cacheKey, TRUE );
$cachedResult['fromCache'] = 1;
Output::i()->json( $cachedResult );
}
catch( \OutOfRangeException $e ){}
catch( OutOfRangeException $e ){}
/* Fetch the result and cache it, then return it */
try
{
$title = NULL;
$isEmbed = FALSE;
$url = \IPS\Http\Url::createFromString( \IPS\Request::i()->url, TRUE, TRUE );
$url = Url::createFromString( Request::i()->url, TRUE, TRUE );
$embed = NULL;
$error = NULL;
if ( !\IPS\Request::i()->noEmbed )
if ( !Request::i()->noEmbed )
{
try
// First make sure this url can be embedded under the current settings
if ( $url instanceof Url\Internal and !$internalEmbedAllowed )
{
if ( \IPS\Request::i()->image and \IPS\Request::i()->width and \IPS\Request::i()->height )
{
\IPS\Text\Parser::isAllowedImageUrl( $url );
$embed = \IPS\Text\Parser::imageEmbed( $url, \intval( \IPS\Request::i()->width ), \intval( \IPS\Request::i()->height ) );
}
else
{
$embed = \IPS\Text\Parser::embeddableMedia( $url );
}
$embed = null;
}
catch( \UnexpectedValueException $e )
else if ( !( $url instanceof Url\Internal ) and ( !$externalEmbedAllowed or ( $imageRequest and !$externalImageAllowed ) ) )
{
switch( $e->getMessage() )
{
case 'embed__fail_404':
$error = \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->get( 'embed__fail_' . $e->getCode() ) ) ) );
break;
case 'embed__fail_403':
$error = \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( $url->data['host'], \IPS\Member::loggedIn()->language()->get( 'embed__fail_' . $e->getCode() ) ) ) );
break;
case 'embed__fail_500':
$error = \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( $url->data['host'] ) ) );
break;
default:
$error = \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() );
break;
}
$embed = null;
}
catch ( \Exception $e )
else
{
/* Log it if debug logging is enabled so we can see what happened. Maybe save another dev fifteen minutes of "why isn't this working" */
\IPS\Log::debug( $e, 'embed_fail' );
try
{
if ( $imageRequest )
{
Parser::isAllowedContentUrl( $url );
$embed = Parser::imageEmbed(
$url instanceof Url\Internal ? $url : (string) Request::i()->url,
intval( Request::i()->width ),
intval( Request::i()->height )
);
}
else
{
$embed = Parser::embeddableMedia( $url );
}
}
catch ( UnexpectedValueException $e )
{
switch ( $e->getMessage() )
{
case 'embed__fail_404':
$error = Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( Member::loggedIn()->language()->get( 'embed__fail_' . $e->getCode() ) ) ) );
break;
case 'embed__fail_403':
$error = Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( $url->data['host'], Member::loggedIn()->language()->get( 'embed__fail_' . $e->getCode() ) ) ) );
break;
case 'embed__fail_500':
$error = Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( $url->data['host'] ) ) );
break;
case 'embed__fail_400':
$error = Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE, array( 'sprintf' => array( 'Bad Request' ) ) );
break;
default:
$error = Member::loggedIn()->language()->addToStack( $e->getMessage() );
break;
}
}
catch ( Exception $e )
{
/* Log it if debug logging is enabled so we can see what happened. Maybe save another dev fifteen minutes of "why isn't this working" */
Log::debug( $e, 'embed_fail' );
}
}
}
if ( $embed OR $error )
{
$insert = $embed;
$isEmbed = $error ? FALSE : TRUE;
$insert = $embed ?: "";
$isEmbed = !$error;
}
else
{
$title = \IPS\Request::i()->title ?: (string) $url;
$title = Request::i()->title ?: (string) $url;
$insert = "<a href='{$url}' ipsNoEmbed='true'>{$title}</a>";
}
$result = array( 'preview' => trim( $insert ), 'title' => $title, 'embed' => $isEmbed, 'errorMessage' => $error ? \IPS\Member::loggedIn()->language()->addToStack( 'embed_failure_message', FALSE, array( 'sprintf' => array( $error ) ) ) : NULL );
$result = array(
'preview' => trim( $insert ),
'title' => $title,
'embed' => $isEmbed,
'errorMessage' => NULL,
'allowCustom' => ( !isset( Request::i()->noExternal ) and FormEditor::memberHasPermission( 'og_embed' ) and !$isEmbed and Parser::isAllowedContentUrl( $url ) ),
);
if( $error )
{
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $result['errorMessage'] );
\IPS\Log::debug( $url . "\n" . $result['errorMessage'], 'embed_failure' );
$result['errorMessage'] = Member::loggedIn()->language()->addToStack( 'embed_failure_message', FALSE, array( 'sprintf' => array( $error ) ) );
Member::loggedIn()->language()->parseOutputForDisplay( $result['errorMessage'] );
Log::debug( $url . "\n" . $result['errorMessage'], 'embed_fail' );
}
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $result, \IPS\DateTime::create()->add( new \DateInterval( 'PT10S' ) ), TRUE );
Cache::i()->storeWithExpire( $cacheKey, $result, DateTime::create()->add( new DateInterval( 'P10D' ) ), true );
\IPS\Output::i()->json( $result );
Output::i()->json( $result );
}
catch ( \Exception $e )
catch ( Exception $e )
{
\IPS\Output::i()->json( $e->getMessage(), 500 );
Output::i()->json( $e->getMessage(), 500 );
}
}
/**
* Get data from an external endpoint to embed in the editor
*
* @return void
*/
protected function getLinkContent() : void
{
/* The csrf check is important to prevent spamming this endpoint, causing all sorts of requests */
Session::i()->csrfCheck();
$content = "";
/* Have we recently checked and validated this link? */
$cacheKey = md5( 'content' . Request::i()->url . Member::loggedIn()->language()->id );
try
{
$cachedResult = Cache::i()->getWithExpire( $cacheKey, TRUE );
Output::i()->json( [ 'content' => $cachedResult ] );
}
catch( OutOfRangeException $e ){}
/* Let's make sure this IP address isn't spamming the endpoint */
if ( Redis::isEnabled() )
{
try
{
Redis::i()->zIncrBy( 'linkContentRequest', 1, Request::i()->ipAddress(), 180 );
/* If the IP address has attempted to get link content 50 times in the last 3 minutes, return nothing. Perhaps in the future, the editor can accept a return code and display a message "Chill out man" or some such */
if ( Redis::i()->zScore( 'linkContentRequest', Request::i()->ipAddress() ) > 50 )
{
Output::i()->json( [ 'content' => '' ] );
}
}
catch( Exception $e ) {}
}
/* Still here? Best get the link */
try
{
if ( !Request::i()->url )
{
throw new DomainException;
}
$request = Url::external( Request::i()->url )->requestUntrusted();
$response = $request->get();
if ( is_string( $response->content ) and str_starts_with( (string) $response->httpResponseCode, '2' ) and str_starts_with( mb_strtolower( $response->httpHeaders['Content-Type'] ), 'text/html' ) )
{
$content = $response->content;
}
}
catch ( Exception ) { }
Cache::i()->storeWithExpire( $cacheKey, $content, DateTime::create()->add( new DateInterval( 'PT5M' ) ) );
Output::i()->json([ "content" => $content ]);
}
/**
* Get Emoji
*
* @return void
*/
protected function emoji()
protected function emoji() : void
{
$emoji = ( \IPS\Settings::i()->emoji_style == 'disabled' or \IPS\Settings::i()->getFromConfGlobal('sql_utf8mb4') !== TRUE ) ? array() : json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/core/data/emoji.json' ), TRUE );
$setsToAdd = array();
foreach ( \IPS\Db::i()->select( '*', 'core_emoticons', NULL, 'emo_set_position,emo_position' ) as $row )
try
{
$shortcode = ( \IPS\Settings::i()->emoji_shortcodes and preg_match( '/^:.*:$/', $row['typed'] ) );
try
$emojis = Cache::i()->getWithExpire( 'core_editor_emoji_sets' );
if ( !is_array( $emojis ) )
{
$categoryName = \IPS\Member::loggedIn()->language()->get( 'core_emoticon_group_' . $row['emo_set'] );
throw new OutOfRangeException;
}
catch( \UnderflowException $e )
{
$categoryName = '_default';
}
$setsToAdd[ $row['emo_set'] ][] = array(
'code' => 'custom-' . $row['emo_set'] . '-' . $row['id'],
'name' => $row['typed'],
'skinTone' => false,
'shortNames' => $shortcode ? array( trim( $row['typed'], ':' ) ) : array(),
'ascii' => $shortcode ? array() : array( $row['typed'] ),
'categoryName' => $categoryName,
'image' => \IPS\File::get( 'core_Emoticons', $row['image'] )->url,
'image2x' => $row['image_2x'] ? \IPS\File::get( 'core_Emoticons', $row['image_2x'] )->url : FALSE,
'width' => $row['width'],
'height' => $row['height'],
);
}
foreach ( $setsToAdd as $setKey => $emojiInSet )
catch ( OutOfRangeException )
{
$emoji[] = array(
'category' => $setKey,
'emoji' => $emojiInSet
);
}
\IPS\Output::i()->json( $emoji );
$emojis = array();
$categoryNames = array();
foreach ( Db::i()->select( '*', 'core_emoticons', NULL, 'emo_set_position,emo_position' ) as $row )
{
try
{
$categoryName = $categoryNames[ $row['emo_set'] ] ?? Member::loggedIn()->language()->get( 'core_emoticon_group_' . $row['emo_set'] );
}
catch ( OutOfRangeException )
{
$categoryName = '_default';
}
$code = 'custom-' . $row['emo_set'] . '-' . $row['id'];
$categoryNames[ $row['emo_set'] ] = $categoryName;
$emojis[ $categoryName ][ $code ] = array(
'unicode' => $code,
'description' => trim( $row['typed'], ":" ),
'skinTones' => false,
'hairStyles' => false,
'sortKey' => $row['emo_position'],
'image' => array(
'image' => (string) File::get( 'core_Emoticons', $row['image'] )->url,
'image2x' => (string) ( $row['image_2x'] ? File::get( 'core_Emoticons', $row['image_2x'] )->url : "" ),
'width' => $row['width'] ?: 20,
'height' => $row['height'] ?: 20
)
);
}
Cache::i()->storeWithExpire( "core_editor_emoji_sets", $emojis, ( new DateTime )->add( new DateInterval( "P7D" ) ) );
}
Output::i()->json( $emojis );
}
/**
@@ -260,43 +371,43 @@ class _editor extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function myMedia()
protected function myMedia() : void
{
/* Init */
$perPage = 12;
$search = isset( \IPS\Request::i()->search ) ? \IPS\Request::i()->search : null;
$search = isset( Request::i()->search ) ? Request::i()->search : null;
/* Get all our available sources */
$mediaSources = array();
foreach ( \IPS\Application::allExtensions( 'core', 'EditorMedia' ) as $k => $class )
foreach ( Application::allExtensions( 'core', 'EditorMedia' ) as $k => $class )
{
if ( $class->count( \IPS\Member::loggedIn(), isset( \IPS\Request::i()->postKey ) ? \IPS\Request::i()->postKey : '' ) )
if ( $class->count( Member::loggedIn(), isset( Request::i()->postKey ) ? Request::i()->postKey : '' ) )
{
$mediaSources[] = $k;
}
}
/* Work out what tab we're on */
if ( !\IPS\Request::i()->tab )
if ( !Request::i()->tab )
{
if( !\count( $mediaSources ) )
if( !count( $mediaSources ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMedia( \IPS\Request::i()->editorId, $mediaSources, NULL, NULL, NULL );
Output::i()->output = Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMedia( Request::i()->editorId, $mediaSources, NULL, NULL, NULL );
return;
}
$sources = $mediaSources;
\IPS\Request::i()->tab = array_shift( $sources );
Request::i()->tab = array_shift( $sources );
}
$exploded = explode( '_', \IPS\Request::i()->tab );
$exploded = explode( '_', Request::i()->tab );
$classname = "IPS\\{$exploded[0]}\\extensions\\core\\EditorMedia\\{$exploded[1]}";
$extension = new $classname;
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=editor&do=myMedia&tab=" . \IPS\Request::i()->tab . "&key=" . \IPS\Request::i()->key . "&postKey=" . \IPS\Request::i()->postKey . "&existing=1" );
$url = Url::internal( "app=core&module=system&controller=editor&do=myMedia&tab=" . Request::i()->tab . "&key=" . Request::i()->key . "&postKey=" . Request::i()->postKey . "&existing=1" );
/* Count how many we have */
$count = $extension->count( \IPS\Member::loggedIn(), isset( \IPS\Request::i()->postKey ) ? \IPS\Request::i()->postKey : '', $search );
$count = $extension->count( Member::loggedIn(), isset( Request::i()->postKey ) ? Request::i()->postKey : '', $search );
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
$page = isset( Request::i()->page ) ? intval( Request::i()->page ) : 1;
if( $page < 1 )
{
@@ -304,49 +415,49 @@ class _editor extends \IPS\Dispatcher\Controller
}
/* Display */
if ( isset( \IPS\Request::i()->existing ) )
if ( isset( Request::i()->existing ) )
{
if ( isset( \IPS\Request::i()->search ) || ( isset( \IPS\Request::i()->page ) && \IPS\Request::i()->page !== 1 ) )
if ( isset( Request::i()->search ) || ( isset( Request::i()->page ) && Request::i()->page !== 1 ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaResults(
$extension->get( \IPS\Member::loggedIn(), $search, isset( \IPS\Request::i()->postKey ) ? \IPS\Request::i()->postKey : '', $page, $perPage ),
\IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
Output::i()->output = Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaResults(
$extension->get( Member::loggedIn(), $search, isset( Request::i()->postKey ) ? Request::i()->postKey : '', $page, $perPage ),
Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
$url,
ceil( $count / $perPage ),
$page,
$perPage
),
$url,
\IPS\Request::i()->tab
Request::i()->tab
);
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaContent(
$extension->get( \IPS\Member::loggedIn(), $search, isset( \IPS\Request::i()->postKey ) ? \IPS\Request::i()->postKey : '', $page, $perPage ),
\IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
Output::i()->output = Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaContent(
$extension->get( Member::loggedIn(), $search, isset( Request::i()->postKey ) ? Request::i()->postKey : '', $page, $perPage ),
Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
$url,
ceil( $count / $perPage ),
$page,
$perPage
),
$url,
\IPS\Request::i()->tab
Request::i()->tab
);
}
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMedia( \IPS\Request::i()->editorId, $mediaSources, \IPS\Request::i()->tab, $url, \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaContent(
$extension->get( \IPS\Member::loggedIn(), $search, isset( \IPS\Request::i()->postKey ) ? \IPS\Request::i()->postKey : '', $page, $perPage ),
\IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
Output::i()->output = Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMedia( Request::i()->editorId, $mediaSources, Request::i()->tab, $url, Theme::i()->getTemplate( 'editor', 'core', 'global' )->myMediaContent(
$extension->get( Member::loggedIn(), $search, isset( Request::i()->postKey ) ? Request::i()->postKey : '', $page, $perPage ),
Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
$url,
ceil( $count / $perPage ),
$page,
$perPage
),
$url,
\IPS\Request::i()->tab
Request::i()->tab
) );
}
}
@@ -356,55 +467,68 @@ class _editor extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function mention()
protected function mention() : void
{
$results = '';
if ( mb_strlen( \IPS\Request::i()->input ) > 0 )
$results = [];
if ( mb_strlen( Request::i()->input ) > 0 )
{
$memberIds = [];
if ( isset( \IPS\Request::i()->contentClass ) AND isset( \IPS\Request::i()->contentId ) )
if ( isset( Request::i()->contentClass ) AND isset( Request::i()->contentId ) )
{
$class = \IPS\Request::i()->contentClass;
if ( \class_exists( $class ) AND \IPS\IPS::classUsesTrait( $class, 'IPS\Content\Statistics' ) )
$class = Request::i()->contentClass;
if ( class_exists( $class ) AND IPS::classUsesTrait( $class, 'IPS\Content\Statistics' ) )
{
try
{
$item = $class::load( \IPS\Request::i()->contentId );
foreach( $item->mostRecent( \IPS\Request::i()->input, 10, FALSE ) AS $row )
$item = $class::load( Request::i()->contentId );
foreach( $item->mostRecent( Request::i()->input, 10, false ) AS $member )
{
$memberIds[] = $row->member_id;
$results .= \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->mentionRow( $row );
$memberIds[] = $member->member_id;
$results[] = [
'userId' => (string) $member->member_id,
'name' => $member->name,
'link' => (string) $member->url(),
'hoverLink' => (string) $member->url()->setQueryString([ 'do' => 'hovercard' ]),
'photo' => (string) $member->photo,
];
}
}
catch( \OutOfRangeException | \BadMethodCallException $e ) { }
catch( OutOfRangeException | BadMethodCallException $e ) { }
}
}
/* If there are less than ten recent participants matching the input, fill it out with non-participants. */
if ( \count( $memberIds ) < 10 )
if ( count( $memberIds ) < 10 )
{
$where = array( \IPS\Db::i()->like( 'name', \IPS\Request::i()->input ) );
$where = array( Db::i()->like( 'name', Request::i()->input ) );
$where[] = array( 'temp_ban !=?', '-1' );
if ( \count( $memberIds ) )
if ( count( $memberIds ) )
{
$where[] = array( \IPS\Db::i()->in( 'member_id', $memberIds, TRUE ) );
$where[] = array( Db::i()->in( 'member_id', $memberIds, TRUE ) );
}
foreach ( \IPS\Db::i()->select( '*', 'core_members', $where, 'name', 10 - \count( $memberIds ) ) as $row )
foreach ( Db::i()->select( '*', 'core_members', $where, 'name', 10 - count( $memberIds ) ) as $row )
{
$results .= \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->mentionRow( \IPS\Member::constructFromData( $row ) );
$member = Member::constructFromData( $row );
$results[] = [
'userId' => (string) $member->member_id,
'name' => $member->name,
'link' => (string) $member->url(),
'hoverLink' => (string) $member->url()->setQueryString([ 'do' => 'hovercard' ]),
'photo' => (string) $member->photo,
];
}
}
}
\IPS\Output::i()->sendOutput( $results );
Output::i()->json( $results );
}
/**
* @brief Languages supported by giphy
* @see https://developers.giphy.com/docs/optional-settings/#language-support
*/
protected static $giphyLanguages = array( 'en', 'es', 'pt', 'id', 'fr', 'ar', 'tr', 'th', 'vi', 'hi', 'bn', 'da', 'fa', 'tl', 'fi',
protected static array $giphyLanguages = array( 'en', 'es', 'pt', 'id', 'fr', 'ar', 'tr', 'th', 'vi', 'hi', 'bn', 'da', 'fa', 'tl', 'fi',
'de', 'it', 'ja', 'ru', 'ko', 'pl', 'nl', 'ro', 'hu', 'sv', 'cs', 'iw', 'ms', 'no', 'uk', 'zh-CN', 'zh-TW'
);
@@ -413,78 +537,105 @@ class _editor extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function giphy()
protected function giphy() : void
{
if ( \IPS\Settings::i()->giphy_enabled )
if ( Settings::i()->giphy_enabled )
{
$limit = isset( \IPS\Request::i()->limit ) ? \IPS\Request::i()->limit : 30;
$offset = isset( \IPS\Request::i()->offset ) ? \IPS\Request::i()->offset : 0;
$q = urlencode( \IPS\Request::i()->search );
/* Return the trending images if there's no search term */
if ( !$q OR $q == '' )
try
{
$url = \IPS\Http\Url::external( "https://api.giphy.com/v1/gifs/trending" );
}
else
{
$url = \IPS\Http\Url::external( "https://api.giphy.com/v1/gifs/search" )->setQueryString( 'q', $q );
}
$limit = isset( Request::i()->limit ) ? Request::i()->limit : 30;
$offset = isset( Request::i()->offset ) ? Request::i()->offset : 0;
$parameters = array(
'api_key' => ( \IPS\Settings::i()->giphy_apikey ? \IPS\Settings::i()->giphy_apikey : \IPS\Settings::i()->giphy_apikey_default ),
'limit' => $limit,
'offset' => $offset
);
$q = urlencode( Request::i()->search );
$ourLocale = \IPS\Member::loggedIn()->language()->short;
if ( preg_match( '/^\w{2}[-_]\w{2}($|\.)/i',$ourLocale ) ) // This will only work for Unix-style locales
{
$langCode = \strtolower( \substr( $ourLocale, 0, 2 ) );
/* Giphy supports zh-CN and zh-TW, so we have to handle this one special */
if( $langCode == 'zh' )
/* Return the trending images if there's no search term */
if ( !$q or $q == '' )
{
$langCode = \strtolower( \substr( $ourLocale, 0, 2 ) ) . '-' . \strtoupper( \substr( $ourLocale, 3, 2 ) );
$url = Url::external( "https://api.giphy.com/v1/gifs/trending" );
}
else
{
$url = Url::external( "https://api.giphy.com/v1/gifs/search" )->setQueryString( 'q', $q );
}
if( \in_array( $langCode, static::$giphyLanguages ) )
{
$parameters['lang'] = $langCode;
}
}
$url = $url->setQueryString($parameters);
if ( \IPS\Settings::i()->giphy_rating and \IPS\Settings::i()->giphy_rating !== 'x' )
{
$url = $url->setQueryString( 'rating', \IPS\Settings::i()->giphy_rating );
}
$request = json_decode( $url->request()->get()->content, true );
if ( isset( $request['message'] ) AND $request['message'] )
{
\IPS\Output::i()->json( array('error' => $request['message'] ) );
}
$results = array( 'pagination' => $request['pagination'] );
foreach ( $request['data'] as $row )
{
$results['images'][] = array(
'thumb' => $row['images']['fixed_height_small']['url'],
'url' => $row['images']['fixed_height']['url'],
'title' => $row['title']
$parameters = array(
'api_key' => ( Settings::i()->giphy_apikey ? Settings::i()->giphy_apikey : Settings::i()->giphy_apikey_default ),
'limit' => $limit,
'offset' => $offset
);
}
if ( empty( $results['images'] ) )
$ourLocale = Member::loggedIn()->language()->short;
if ( preg_match( '/^\w{2}[-_]\w{2}($|\.)/i', $ourLocale ) ) // This will only work for Unix-style locales
{
$langCode = strtolower( substr( $ourLocale, 0, 2 ) );
/* Giphy supports zh-CN and zh-TW, so we have to handle this one special */
if ( $langCode == 'zh' )
{
$langCode = strtolower( substr( $ourLocale, 0, 2 ) ) . '-' . strtoupper( substr( $ourLocale, 3, 2 ) );
}
if ( in_array( $langCode, static::$giphyLanguages ) )
{
$parameters['lang'] = $langCode;
}
}
$url = $url->setQueryString( $parameters );
if ( Settings::i()->giphy_rating and Settings::i()->giphy_rating !== 'x' )
{
$url = $url->setQueryString( 'rating', Settings::i()->giphy_rating );
}
$cacheKey = "IPS_GIPHY__" . md5( $url );
$results = null;
try
{
$results = json_decode( Cache::i()->getWithExpire( $cacheKey, true ), true );
}
catch ( OutOfRangeException ) {}
if ( !is_array( $results ) )
{
$data = $url->request()->get()->decodeJson();
if ( isset( $data['message'] ) and $data['message'] )
{
Output::i()->json( array( 'error' => $data['message'] ) );
}
if ( !isset( $data['data'] ) OR !is_array( $data['data'] ) )
{
Output::i()->json( ['error' => 'could not fetch results'], 500 );
}
$results = array( 'pagination' => $data['pagination'] ?? '', 'images' => [] );
foreach ( $data['data'] as $row )
{
if ( !isset( $row['images']['fixed_height'] ) or !isset( $row['images']['fixed_height_small'] ) or !isset( $row['images']['fixed_height']['url'] ) )
{
continue;
}
$results['images'][] = array(
'thumb' => $row['images']['fixed_height_small']['url'] ?? $row['images']['fixed_height']['url'],
'url' => $row['images']['fixed_height']['url'],
'title' => $row['title'],
'width' => intval($row['images']['fixed_height_small']['width'] ?? 150),
'height' => intval($row['images']['fixed_height_small']['height'] ?? 100)
);
}
Cache::i()->storeWithExpire( $cacheKey, json_encode( $results ), ( new DateTime() )->add( new DateInterval( 'PT1H' ) ), true );
}
Output::i()->json( $results );
}
catch ( Exception $e )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ) );
Output::i()->json( [ 'error' => var_export( $e, true ), 'request' => $data ?? '' ], 500 );
}
\IPS\Output::i()->json( $results );
}
}
@@ -493,42 +644,43 @@ class _editor extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function storedReplies()
protected function storedReplies() : void
{
if ( isset( \IPS\Request::i()->id ) )
if ( isset( Request::i()->id ) )
{
try
{
$reply = \IPS\core\StoredReplies::loadAndCheckPerms( \IPS\Request::i()->id );
$reply = StoredReplies::loadAndCheckPerms( Request::i()->id );
\IPS\Output::i()->json( [
Output::i()->json( [
'id' => $reply->id,
'title' => $reply->title,
'reply' => $reply->text
] );
}
catch( \Exception $e )
catch( Exception )
{
\IPS\Output::i()->json( [ 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ] );
Output::i()->json( [ 'error' => "An unexpected error occurred" ], 500 );
}
}
else
{
$results = [];
foreach ( \IPS\core\StoredReplies::roots() as $reply )
try
{
if ( $reply->enabled )
$results = [];
foreach ( StoredReplies::roots() as $reply )
{
$results[] = ['id' => $reply->id, 'title' => $reply->title];
if ( $reply->enabled )
{
$results[] = [ 'id' => $reply->id, 'title' => $reply->title ];
}
}
Output::i()->json( [ "results" => $results ] );
}
if ( empty( $results ) )
catch ( Exception )
{
\IPS\Output::i()->json( [ 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ] );
Output::i()->json( [ "error" => 'An unexpected error occurred', "results" => [] ], 500 );
}
\IPS\Output::i()->json( $results );
}
}
}
@@ -11,58 +11,79 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\Content\Controller;
use IPS\Data\Cache;
use IPS\DateTime;
use IPS\Http\Url;
use IPS\Output;
use IPS\Request;
use IPS\Text\Parser;
use IPS\Theme;
use OutOfRangeException;
use UnexpectedValueException;
use function defined;
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;
}
/**
* Embed iframe display
*/
class _embed extends \IPS\Content\Controller
class embed extends Controller
{
/**
* Embed iframe display
*
* @return void
* @return mixed
*/
protected function manage()
protected function manage() : mixed
{
/* Check cache */
$cacheKey = 'embed_' . md5( \IPS\Request::i()->url );
/* Check cache for the current scheme */
$scheme = Theme::i()->getCurrentCSSScheme();
$cacheKey = 'embed_' . md5( Request::i()->url ) . "_" . $scheme;
try
{
$return = \IPS\Data\Cache::i()->getWithExpire( $cacheKey, TRUE );
$return = Cache::i()->getWithExpire( $cacheKey, TRUE );
}
/* Not in cache - fetch */
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
try
{
$return = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( \IPS\Request::i()->url, FALSE, TRUE ), TRUE );
$return = Parser::embeddableMedia( Url::createFromString( Request::i()->url, FALSE, TRUE ), TRUE );
}
catch( \UnexpectedValueException $e )
catch( UnexpectedValueException $e )
{
$return = '';
}
/* And cache */
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $return, \IPS\DateTime::create()->add( new \DateInterval('P1D') ), TRUE );
Cache::i()->storeWithExpire( $cacheKey, $return, DateTime::create()->add( new DateInterval('P1D') ), TRUE );
}
/* Output */
$js = array(
\IPS\Output::i()->js( 'js/commonEmbedHandler.js', 'core', 'interface' ),
\IPS\Output::i()->js( 'js/externalEmbedHandler.js', 'core', 'interface' )
Output::i()->js( 'js/commonEmbedHandler.js', 'core', 'interface' ),
Output::i()->js( 'js/externalEmbedHandler.js', 'core', 'interface' )
);
/* We don't want search engines indexing this */
\IPS\Output::i()->metaTags['robots'] = 'noindex';
Output::i()->metaTags['robots'] = 'noindex';
/* Intentionally replace the cssFiles array with a single file here, since we don't need the complete CSS framework in external embeds */
\IPS\Output::i()->cssFiles = \IPS\Theme::i()->css( 'styles/embeds.css', 'core', 'front' );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->embedExternal( $return, $js ), 200 );
Output::i()->cssFiles = array_merge(
Theme::i()->css( 'styles/embeds.css', 'core', 'front' ),
Theme::i()->css( "framework/1-2-settings.css", "core", "global" ),
Theme::i()->css( "framework/1-3-variables.css", "core", "global" ),
Theme::i()->css( "framework/1-4-colors.css", "core", "global" ),
Theme::i()->css( "core.css", "core", "front" ),
);
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core', 'front' )->embedExternal( $return, $js ) );
}
}
@@ -1,113 +0,0 @@
<?php
/**
* @brief Facebook Authentication
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 09 August 2018
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Facebook auth for ACP
*/
class _facebook extends \IPS\Dispatcher\Controller
{
/**
* View Announcement
*
* @return void
*/
protected function manage()
{
if ( ! \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->addRef( \IPS\Http\Url::internal( 'app=core&module=system&controller=facebook' ) ) );
}
/* Check the member has permission to manage promote settings */
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'promote_manage' ) )
{
\IPS\Output::i()->error( 'no_module_permission', '3C396/1', 403, '' );
}
$promote = \IPS\core\Promote::getPromoter( 'Facebook' );
$facebook = $promote::getLoginHandler();
/* Required handler present and enabled? */
if( $facebook === NULL )
{
\IPS\Output::i()->error( 'promote_facebook_setup_app', '3C396/2', 403, '' );
}
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=facebook' );
try
{
/* Get a request token */
if ( !isset( \IPS\Request::i()->code ) )
{
$target = \IPS\Http\Url::external('https://www.facebook.com/dialog/oauth')->setQueryString( array(
'client_id' => $facebook->settings['client_id'],
'response_type' => 'code',
'redirect_uri' => (string) \IPS\Http\Url::internal( 'oauth/callback/', 'none' ),
'state' => $facebook->id . '-' . base64_encode( $url ) . '-' . \IPS\Session::i()->csrfKey . '-x',
'scope' => 'manage_pages publish_pages'
) );
\IPS\Output::i()->redirect( $target );
}
/* CSRF Check */
if ( \IPS\Request::i()->csrfKey !== \IPS\Session::i()->csrfKey )
{
throw new \IPS\Login\Exception( 'CSRF_FAIL', \IPS\Login\Exception::INTERNAL_ERROR );
}
/* Did we get an error? */
if ( isset( \IPS\Request::i()->error ) )
{
if ( \IPS\Request::i()->error === 'access_denied' )
{
return NULL;
}
else
{
\IPS\Log::log( print_r( $_GET, TRUE ), 'oauth' );
throw new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::INTERNAL_ERROR );
}
}
/* If we have a code, swap it for an access token, otherwise, decode what we have */
if ( isset( \IPS\Request::i()->code ) )
{
$accessToken = $facebook->exchangeMemberToken( \IPS\Request::i()->code );
}
/* Store the settings */
$promote->saveSettings( array(
'member_token' => $accessToken
) );
/* Show a done page */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_facebook_sorted');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promote' )->promoteFacebookComplete();
}
catch ( \Exception $e )
{
\IPS\Log::log( $e, 'facebook_promote' );
\IPS\Output::i()->error( 'generic_error', '4C375/1', 403, $e->getMessage() );
}
}
}
@@ -11,36 +11,47 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Content;
use IPS\core\Followed\Table;
use IPS\Dispatcher\Controller;
use IPS\IPS;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Theme;
use function defined;
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;
}
/**
* My followed content
*/
class _followed extends \IPS\Dispatcher\Controller
class followed extends Controller
{
/**
* My followed content
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Guests can't follow things */
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission_guest', '2C266/1', 403, '' );
Output::i()->error( 'no_module_permission_guest', '2C266/1', 403, '' );
}
/* Get the different types */
$types = array();
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
foreach ( Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
if( is_subclass_of( $class, 'IPS\Content\Followable' ) )
if( IPS::classUsesTrait( $class, 'IPS\Content\Followable' ) )
{
$types[ $class::$application . '_' . $class::$module ][ mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) ) ] = $class;
@@ -60,19 +71,19 @@ class _followed extends \IPS\Dispatcher\Controller
}
/* Don't forget Members - add this on to the end so it never defaults UNLESS we only have apps that do not have followable content */
$types['core'] = array( 'core_member' => "\IPS\Member" );
$types['core'] = array( 'core_member' => "\IPS\Member", 'core_tag' => "IPS\Content\Tag" );
/* What type are we looking at? */
$currentAppModule = NULL;
$currentType = NULL;
if ( isset( \IPS\Request::i()->type ) )
if ( isset( Request::i()->type ) )
{
foreach ( $types as $appModule => $_types )
{
if ( array_key_exists( \IPS\Request::i()->type, $_types ) )
if ( array_key_exists( Request::i()->type, $_types ) )
{
$currentAppModule = $appModule;
$currentType = \IPS\Request::i()->type;
$currentType = Request::i()->type;
break;
}
}
@@ -93,26 +104,22 @@ class _followed extends \IPS\Dispatcher\Controller
$currentClass = $types[ $currentAppModule ][ $currentType ];
$output = new \IPS\core\Followed\Table( $currentClass, explode( '_', $currentType ) );
$output = new Table( $currentClass, explode( '_', $currentType ) );
/* If we've clicked from the tab section */
if ( \IPS\Request::i()->isAjax() && \IPS\Request::i()->change_section )
if ( Request::i()->isAjax() && Request::i()->change_section )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->followedContentSection( $types, $currentAppModule, $currentType, $output );
Output::i()->output = Theme::i()->getTemplate( 'system' )->followedContentSection( $types, $currentAppModule, $currentType, $output );
}
else
{
/* Display */
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/followedcontent_responsive.css' ) );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_system.js', 'core' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu_followed_content');
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('menu_followed_content') );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->followedContent( $types, $currentAppModule, $currentType, $output );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/profiles.css' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_system.js', 'core' ) );
Output::i()->title = Member::loggedIn()->language()->addToStack('menu_followed_content');
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('menu_followed_content') );
Output::i()->output = Theme::i()->getTemplate( 'system' )->followedContent( $types, $currentAppModule, $currentType, $output );
}
}
}
@@ -11,37 +11,47 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use function defined;
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;
}
/**
* Guidelines
*/
class _guidelines extends \IPS\Dispatcher\Controller
class guidelines extends Controller
{
/**
* Guidelines
*
* @return void
*/
protected function manage()
protected function manage() : void
{
if ( \IPS\Settings::i()->gl_type == "none" )
if ( Settings::i()->gl_type == "none" )
{
\IPS\Output::i()->error( 'node_error', '2C380/1', 404, 'guidelines_set_to_none_admin' );
Output::i()->error( 'node_error', '2C380/1', 404, 'guidelines_set_to_none_admin' );
}
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=guidelines', NULL, 'guidelines' ), array(), 'loc_viewing_guidelines' );
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=guidelines', NULL, 'guidelines' ), array(), 'loc_viewing_guidelines' );
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('guidelines') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('guidelines');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->guidelines( \IPS\Settings::i()->gl_guidelines );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('guidelines') );
Output::i()->title = Member::loggedIn()->language()->addToStack('guidelines');
Output::i()->output = Theme::i()->getTemplate( 'system' )->guidelines( Settings::i()->gl_guidelines );
}
}
+113 -95
View File
@@ -11,30 +11,48 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use Exception;
use InvalidArgumentException;
use IPS\core\Ignore as IgnoreClass;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Checkbox;
use IPS\Helpers\Table\Db;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use OutOfRangeException;
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;
}
/**
* Ignore Preferences
*/
class _ignore extends \IPS\Dispatcher\Controller
class ignore extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
if ( !\IPS\Member::loggedIn()->member_id )
if ( !Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_module_permission_guest', '2C146/1', 403, '' );
Output::i()->error( 'no_module_permission_guest', '2C146/1', 403, '' );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_ignore.js', 'core' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_ignore.js', 'core' ) );
parent::execute();
}
@@ -44,36 +62,36 @@ class _ignore extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' );
$url = Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' );
/* Build form */
$form = \IPS\core\Ignore::form();
$form = IgnoreClass::form();
if ( $values = $form->values() )
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
try
{
\IPS\core\Ignore::createFromForm( $values );
IgnoreClass::createFromForm( $values );
}
catch( \InvalidArgumentException $e )
catch( InvalidArgumentException $e )
{
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Member::loggedIn()->language()->addToStack( 'cannot_ignore_self' ) ), 403 );
Output::i()->json( array( 'error' => Member::loggedIn()->language()->addToStack( 'cannot_ignore_self' ) ), 403 );
}
else
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() ), '1C146/2', 403, '' );
Output::i()->error( Member::loggedIn()->language()->addToStack( $e->getMessage() ), '1C146/2', 403, '' );
}
}
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
$data = array();
foreach( \IPS\core\Ignore::types() AS $type )
foreach( IgnoreClass::types() AS $type )
{
if ( $values["ignore_{$type}"] )
{
@@ -81,31 +99,31 @@ class _ignore extends \IPS\Dispatcher\Controller
}
}
\IPS\Output::i()->json( array( 'name' => $values['member']->name, 'member_id' => $values['member']->member_id, 'data' => $data ) );
Output::i()->json( array( 'name' => $values['member']->name, 'member_id' => $values['member']->member_id, 'data' => $data ) );
}
else
{
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
}
/* Build table */
$table = new \IPS\Helpers\Table\Db( 'core_ignored_users', $url, array( array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) ) );
$table = new Db( 'core_ignored_users', $url, array( array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) ) );
$table->title = 'ignored_users_current';
$table->langPrefix = 'ignore_';
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' ), 'ignoreTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'system' ), 'ignoreTableRows' );
$table->tableTemplate = array( Theme::i()->getTemplate( 'system', 'core', 'front' ), 'ignoreTable' );
$table->rowsTemplate = array( Theme::i()->getTemplate( 'system' ), 'ignoreTableRows' );
$filters = array();
foreach ( \IPS\core\Ignore::types() as $type )
foreach ( IgnoreClass::types() as $type )
{
$filters[ $type ] = "ignore_{$type}=1";
}
$table->filters = $filters;
/* Display */
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('ignored_users') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('ignored_users');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->ignore( $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'system' ), 'ignoreForm' ) ), (string) $table, ( isset( \IPS\Request::i()->id ) ? \IPS\Request::i()->id : 0 ) );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('ignored_users') );
Output::i()->title = Member::loggedIn()->language()->addToStack('ignored_users');
Output::i()->output = Theme::i()->getTemplate( 'system' )->ignore( $form->customTemplate( array( Theme::i()->getTemplate( 'system' ), 'ignoreForm' ) ), (string) $table, ( isset( Request::i()->id ) ? Request::i()->id : 0 ) );
}
/**
@@ -113,49 +131,49 @@ class _ignore extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function add()
protected function add() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
try
{
/* We have to html_entity_decode because the javascript code sends the encoded name here */
$member = \IPS\Member::load( html_entity_decode( \IPS\Request::i()->name, ENT_QUOTES, 'UTF-8' ), 'name' );
$member = Member::load( html_entity_decode( Request::i()->name, ENT_QUOTES, 'UTF-8' ), 'name' );
/* If \IPS\Member::load() cannot find a member, it just creates a new guest object, never throwing the exception */
if ( !$member->member_id )
{
throw new \OutOfRangeException( 'cannot_ignore_no_user' );
throw new OutOfRangeException( 'cannot_ignore_no_user' );
}
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
if ( $member->member_id == Member::loggedIn()->member_id )
{
throw new \InvalidArgumentException( 'cannot_ignore_self' );
throw new InvalidArgumentException( 'cannot_ignore_self' );
}
if ( !$member->canBeIgnored() )
{
throw new \InvalidArgumentException( 'cannot_ignore_that_member' );
throw new InvalidArgumentException( 'cannot_ignore_that_member' );
}
$ignore = NULL;
try
{
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = IgnoreClass::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
}
catch ( \OutOfRangeException $e ) {}
catch ( OutOfRangeException $e ) {}
$data = array();
foreach ( \IPS\core\Ignore::types() as $t )
foreach ( IgnoreClass::types() as $t )
{
$data[ $t ] = $ignore ? $ignore->$t : FALSE;
}
\IPS\Output::i()->json( $data );
Output::i()->json( $data );
}
catch( \Exception $e )
catch( Exception $e )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() ) ), 403 );
Output::i()->json( array( 'error' => Member::loggedIn()->language()->addToStack( $e->getMessage() ) ), 403 );
}
}
@@ -164,70 +182,70 @@ class _ignore extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function edit()
protected function edit() : void
{
try
{
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
/* If \IPS\Member::load() cannot find a member, it just creates a new guest object, never throwing the exception */
if ( !$member->member_id )
{
throw new \OutOfRangeException( 'cannot_ignore_no_user' );
throw new OutOfRangeException( 'cannot_ignore_no_user' );
}
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
if ( $member->member_id == Member::loggedIn()->member_id )
{
throw new \InvalidArgumentException( 'cannot_ignore_self' );
throw new InvalidArgumentException( 'cannot_ignore_self' );
}
if ( !$member->canBeIgnored() )
{
throw new \InvalidArgumentException( 'cannot_ignore_that_member' );
throw new InvalidArgumentException( 'cannot_ignore_that_member' );
}
$ignore = NULL;
try
{
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = IgnoreClass::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
}
catch ( \OutOfRangeException $e ) {}
catch ( OutOfRangeException $e ) {}
$form = new \IPS\Helpers\Form( NULL, 'ignore_edit' );
$form->class = 'ipsForm_vertical';
$form = new Form( NULL, 'ignore_edit' );
$form->class = 'ipsForm--vertical ipsForm--edit-ignore';
foreach ( \IPS\core\Ignore::types() as $type )
foreach ( IgnoreClass::types() as $type )
{
$form->add( new \IPS\Helpers\Form\Checkbox( "ignore_{$type}", $ignore ? $ignore->$type : FALSE ) );
$form->add( new Checkbox( "ignore_{$type}", $ignore ? $ignore->$type : FALSE ) );
}
/* Save values */
if( $values = $form->values() )
{
foreach ( \IPS\core\Ignore::types() as $type )
foreach ( IgnoreClass::types() as $type )
{
$ignore->$type = $values["ignore_{$type}"];
}
$ignore->save();
if( !\IPS\Request::i()->isAjax() )
if( !Request::i()->isAjax() )
{
$this->manage();
}
else
{
\IPS\Output::i()->json( 'OK' );
Output::i()->json( 'OK' );
}
}
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('ignored_users') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('add_ignored_user');
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'system' ), 'ignoreEditForm' ) );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('ignored_users') );
Output::i()->title = Member::loggedIn()->language()->addToStack('add_ignored_user');
Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'system' ), 'ignoreEditForm' ) );
}
catch( \Exception $e )
catch( Exception $e )
{
\IPS\Output::i()->json( \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() ), 403 );
Output::i()->json( Member::loggedIn()->language()->addToStack( $e->getMessage() ), 403 );
}
}
@@ -236,38 +254,38 @@ class _ignore extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function remove()
protected function remove() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
Request::i()->confirmedDelete();
try
{
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
/* If \IPS\Member::load() cannot find a member, it just creates a new guest object, never throwing the exception */
if ( !$member->member_id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = IgnoreClass::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
$ignore->delete();
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'results' => 'ok', 'name' => $member->name ) );
Output::i()->json( array( 'results' => 'ok', 'name' => $member->name ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' ), \IPS\Member::loggedIn()->language()->addToStack( 'ignore_removed', FALSE, array( 'sprintf' => array( $member->name ) ) ) );
Output::i()->redirect( Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' ), Member::loggedIn()->language()->addToStack( 'ignore_removed', FALSE, array( 'sprintf' => array( $member->name ) ) ) );
}
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C146/5', 404, '' );
Output::i()->error( 'node_error', '2C146/5', 404, '' );
}
}
@@ -276,74 +294,74 @@ class _ignore extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function ignoreType()
protected function ignoreType() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
try
{
if ( !\in_array( \IPS\Request::i()->type, \IPS\core\Ignore::types() ) )
if ( !in_array( Request::i()->type, IgnoreClass::types() ) )
{
throw new \OutOfRangeException( 'invalid_type' );
throw new OutOfRangeException( 'invalid_type' );
}
$member = \IPS\Member::load( \IPS\Request::i()->member_id );
$member = Member::load( Request::i()->member_id );
if ( !$member->member_id )
{
throw new \OutOfRangeException( 'cannot_ignore_no_user' );
throw new OutOfRangeException( 'cannot_ignore_no_user' );
}
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
if ( $member->member_id == Member::loggedIn()->member_id )
{
throw new \InvalidArgumentException( 'cannot_ignore_self' );
throw new InvalidArgumentException( 'cannot_ignore_self' );
}
if ( !$member->canBeIgnored() )
{
throw new \InvalidArgumentException( 'cannot_ignore_that_member' );
throw new InvalidArgumentException( 'cannot_ignore_that_member' );
}
$type = \IPS\Request::i()->type;
$value = isset( \IPS\Request::i()->off ) ? FALSE : TRUE;
$type = Request::i()->type;
$value = !isset( Request::i()->off );
try
{
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = IgnoreClass::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
$ignore->$type = $value;
$ignore->save();
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$ignore = new \IPS\core\Ignore;
$ignore = new IgnoreClass;
$ignore->$type = $value;
$ignore->owner_id = \IPS\Member::loggedIn()->member_id;
$ignore->owner_id = Member::loggedIn()->member_id;
$ignore->ignore_id = $member->member_id;
$ignore->save();
}
\IPS\Member::loggedIn()->members_bitoptions['has_no_ignored_users'] = FALSE;
\IPS\Member::loggedIn()->save();
Member::loggedIn()->members_bitoptions['has_no_ignored_users'] = FALSE;
Member::loggedIn()->save();
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'result' => 'ok' ) );
Output::i()->json( array( 'result' => 'ok' ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' ), 'ignore_adjusted' );
Output::i()->redirect( Url::internal( "app=core&module=system&controller=ignore", 'front', 'ignore' ), 'ignore_adjusted' );
}
}
catch( \Exception $e )
catch( Exception $e )
{
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'error' => $e->getMessage() ), 403 );
Output::i()->json( array( 'error' => $e->getMessage() ), 403 );
}
else
{
\IPS\Output::i()->error( $e->getMessage(), '1C146/4', 403, '' );
Output::i()->error( $e->getMessage(), '1C146/4', 403, '' );
}
}
}
@@ -11,23 +11,32 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use function defined;
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;
}
/**
* Language Changer
*/
class _language extends \IPS\Dispatcher\Controller
class language extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
parent::execute();
}
@@ -37,20 +46,20 @@ class _language extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
if( \IPS\Member::loggedIn()->member_id )
if( Member::loggedIn()->member_id )
{
\IPS\Member::loggedIn()->language = (int) \IPS\Request::i()->id;
\IPS\Member::loggedIn()->save();
Member::loggedIn()->language = (int) Request::i()->id;
Member::loggedIn()->save();
}
else
{
\IPS\Request::i()->setCookie( 'language', (int) \IPS\Request::i()->id );
Request::i()->setCookie( 'language', (int) Request::i()->id );
}
\IPS\Output::i()->redirect( \IPS\Request::i()->referrer() ?: \IPS\Http\Url::internal( '' ) );
Output::i()->redirect( Request::i()->referrer() ?: Url::internal( '' ) );
}
}
+126 -94
View File
@@ -11,45 +11,79 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\Data\Store;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Extensions\SSOAbstract;
use IPS\Http\Url;
use IPS\Http\Url\Internal;
use IPS\Login as LoginClass;
use IPS\Login\Exception;
use IPS\Login\Handler;
use IPS\Login\Success;
use IPS\Member;
use IPS\Member\Device;
use IPS\MFA\MFAHandler;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
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;
}
/**
* Login
*/
class _login extends \IPS\Dispatcher\Controller
class login extends Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
public bool $isContentPage = FALSE;
/**
* Log In
*
* @return void
* @return void
* @throws \Exception
*/
protected function manage()
protected function manage() : void
{
\IPS\Output::setCacheTime( false );
Output::i()->pageCaching = FALSE;
foreach( Application::allExtensions( 'core', 'SSO', FALSE ) as $ext )
{
/* @var SSOAbstract $ext */
if( $ext->isEnabled() AND $url = $ext->loginUrl() )
{
Output::i()->redirect( $url );
}
}
/* Init login class */
$login = new \IPS\Login( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' ) );
$login = new LoginClass( Url::internal( "app=core&module=system&controller=login", 'front', 'login' ) );
/* What's our referrer? */
$postBeforeRegister = NULL;
$ref = \IPS\Request::i()->referrer( FALSE, FALSE );
$ref = Request::i()->referrer();
if ( !$ref and isset( \IPS\Request::i()->cookie['post_before_register'] ) )
if ( !$ref and isset( Request::i()->cookie['post_before_register'] ) )
{
try
{
$postBeforeRegister = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( 'secret=?', \IPS\Request::i()->cookie['post_before_register'] ) )->first();
$postBeforeRegister = Db::i()->select( '*', 'core_post_before_registering', array( 'secret=?', Request::i()->cookie['post_before_register'] ) )->first();
}
catch( \UnderflowException $e ){}
catch( UnderflowException $e ){}
}
/* Process */
@@ -58,9 +92,9 @@ class _login extends \IPS\Dispatcher\Controller
{
if ( $success = $login->authenticate() )
{
if ( \IPS\Request::i()->referrer( FALSE, TRUE ) )
if ( Request::i()->referrer( FALSE, TRUE ) )
{
$ref = \IPS\Request::i()->referrer( FALSE, TRUE );
$ref = Request::i()->referrer( FALSE, TRUE );
}
elseif ( $postBeforeRegister )
{
@@ -69,53 +103,51 @@ class _login extends \IPS\Dispatcher\Controller
$class = $postBeforeRegister['class'];
$ref = $class::load( $postBeforeRegister['id'] )->url();
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
$ref = \IPS\Http\Url::internal('');
$ref = Url::internal('');
}
}
elseif( !empty( $_SERVER['HTTP_REFERER'] ) )
{
$_ref = \IPS\Http\Url::createFromString( $_SERVER['HTTP_REFERER'] );
$ref = ( $_ref instanceof \IPS\Http\Url\Internal and ( !isset( $_ref->queryString['do'] ) or $_ref->queryString['do'] != 'validating' ) ) ? $_ref : \IPS\Http\Url::internal('');
$_ref = Url::createFromString( $_SERVER['HTTP_REFERER'] );
$ref = ( $_ref instanceof Internal and ( !isset( $_ref->queryString['do'] ) or $_ref->queryString['do'] != 'validating' ) ) ? $_ref : Url::internal('');
}
else
{
$ref = \IPS\Http\Url::internal( '' );
$ref = Url::internal( '' );
}
if ( $success->mfa() )
{
$_SESSION['processing2FA'] = array( 'memberId' => $success->member->member_id, 'anonymous' => $success->anonymous, 'remember' => $success->rememberMe, 'destination' => (string) $ref, 'handler' => $success->handler->id );
\IPS\Output::i()->redirect( $ref->setQueryString( '_mfaLogin', 1 ) );
Output::i()->redirect( $ref->setQueryString( '_mfaLogin', 1 ) );
}
$success->process();
\IPS\Output::i()->redirect( $ref->setQueryString( '_fromLogin', 1 ) );
Output::i()->redirect( $ref->setQueryString( '_fromLogin', 1 ) );
}
}
catch ( \IPS\Login\Exception $e )
catch ( Exception $e )
{
if ( $e->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT )
if ( $e->getCode() === Exception::MERGE_SOCIAL_ACCOUNT )
{
$e->member = $e->member->member_id;
$e->handler = $e->handler->id;
$_SESSION['linkAccounts'] = json_encode( $e );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login&do=link', 'front', 'login' )->setQueryString( 'ref', $ref ), '', 303 );
$_SESSION['linkAccounts'] = json_encode( [ 'member' => $e->member->member_id, 'handler' => $e->handler->id ] );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=login&do=link', 'front', 'login' )->setQueryString( 'ref', $ref ), '', 303 );
}
$error = $e->getMessage();
}
/* Are we already logged in? */
if ( \IPS\Member::loggedIn()->member_id AND ( !\IPS\Request::i()->_err OR \IPS\Request::i()->_err != 'login_as_user_login' ) )
if ( Member::loggedIn()->member_id AND ( !Request::i()->_err OR Request::i()->_err != 'login_as_user_login' ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
Output::i()->redirect( Url::internal('') );
}
/* If there is only one button handler, redirect */
if ( !isset( \IPS\Request::i()->_processLogin ) and !$login->usernamePasswordMethods() and \count( $login->buttonMethods() ) == 1 )
if ( !isset( Request::i()->_processLogin ) and !$login->usernamePasswordMethods() and count( $login->buttonMethods() ) == 1 )
{
$buttonMethod = $login->buttonMethods()[ array_key_first( $login->buttonMethods() ) ];
if( method_exists( $buttonMethod, 'authenticateButton' ) )
@@ -125,17 +157,17 @@ class _login extends \IPS\Dispatcher\Controller
}
/* Display Login Form */
\IPS\Output::i()->allowDefaultWidgets = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('login');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->login( $login, base64_encode( $ref ), $error );
Output::i()->allowDefaultWidgets = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->title = Member::loggedIn()->language()->addToStack('login');
Output::i()->output = Theme::i()->getTemplate( 'system' )->login( $login, base64_encode( $ref ), $error );
/* Don't cache for a short while to ensure sessions work */
\IPS\Request::i()->setCookie( 'noCache', 1 );
Request::i()->setCookie( 'noCache', 1 );
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', NULL, 'login' ), array(), 'loc_logging_in' );
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=login', NULL, 'login' ), array(), 'loc_logging_in' );
}
/**
@@ -143,55 +175,55 @@ class _login extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function mfa()
protected function mfa() : void
{
/* Have we logged in? */
$member = NULL;
if ( isset( $_SESSION['processing2FA'] ) )
{
$member = \IPS\Member::load( $_SESSION['processing2FA']['memberId'] );
$member = Member::load( $_SESSION['processing2FA']['memberId'] );
}
if ( !$member->member_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
}
/* Where do we want to go? */
$destination = \IPS\Http\Url::internal( '' );
$destination = Url::internal( '' );
try
{
$destination = \IPS\Http\Url::createFromString( $_SESSION['processing2FA']['destination'] );
$destination = Url::createFromString( $_SESSION['processing2FA']['destination'] );
}
catch ( \Exception $e ) { }
/* Have we already done 2FA? */
$device = \IPS\Member\Device::loadOrCreate( $member, FALSE );
$output = \IPS\MFA\MFAHandler::accessToArea( 'core', $device->known ? 'AuthenticateFrontKnown' : 'AuthenticateFront', \IPS\Http\Url::internal( 'app=core&module=system&controller=login&do=mfa', 'front', 'login' ), $member );
$device = Device::loadOrCreate( $member, FALSE );
$output = MFAHandler::accessToArea( 'core', $device->known ? 'AuthenticateFrontKnown' : 'AuthenticateFront', Url::internal( 'app=core&module=system&controller=login&do=mfa', 'front', 'login' ), $member );
if ( !$output )
{
( new \IPS\Login\Success( $member, \IPS\Login\Handler::load( $_SESSION['processing2FA']['handler'] ), $_SESSION['processing2FA']['remember'], $_SESSION['processing2FA']['anonymous'], FALSE ) )->process();
\IPS\Output::i()->redirect( $destination->setQueryString( '_fromLogin', 1 ), '', 303 );
( new Success( $member, Handler::load( $_SESSION['processing2FA']['handler'] ), $_SESSION['processing2FA']['remember'], $_SESSION['processing2FA']['anonymous'], FALSE ) )->process();
Output::i()->redirect( $destination->setQueryString( '_fromLogin', 1 ), '', 303 );
}
/* Nope, just send us where we want to go not logged in */
$qs = array( '_mfaLogin' => 1 );
if ( isset( \IPS\Request::i()->_mfa ) )
if ( isset( Request::i()->_mfa ) )
{
$qs['_mfa'] = \IPS\Request::i()->_mfa;
if ( isset( \IPS\Request::i()->_mfaMethod ) )
$qs['_mfa'] = Request::i()->_mfa;
if ( isset( Request::i()->_mfaMethod ) )
{
$qs['_mfaMethod'] = \IPS\Request::i()->_mfaMethod;
$qs['_mfaMethod'] = Request::i()->_mfaMethod;
}
}
elseif ( isset( \IPS\Request::i()->mfa_auth ) )
elseif ( isset( Request::i()->mfa_auth ) )
{
$qs['mfa_auth'] = \IPS\Request::i()->mfa_auth;
$qs['mfa_auth'] = Request::i()->mfa_auth;
}
elseif ( isset( \IPS\Request::i()->mfa_setup ) )
elseif ( isset( Request::i()->mfa_setup ) )
{
$qs['mfa_setup'] = \IPS\Request::i()->mfa_setup;
$qs['mfa_setup'] = Request::i()->mfa_setup;
}
\IPS\Output::i()->redirect( $destination->setQueryString( $qs ) );
Output::i()->redirect( $destination->setQueryString( $qs ) );
}
/**
@@ -199,60 +231,60 @@ class _login extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function link()
protected function link() : void
{
/* Get the member we're linking with */
if ( !isset( $_SESSION['linkAccounts'] ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
}
$details = json_decode( $_SESSION['linkAccounts'], TRUE );
$member = \IPS\Member::load( $details['member'] );
$member = Member::load( $details['member'] );
if ( !$member->member_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
}
/* And then handler to link with */
$handler = \IPS\Login\Handler::load( $details['handler'] );
$handler = Handler::load( $details['handler'] );
/* Init reauthentication */
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login&do=link', 'front', 'login' )->setQueryString( 'ref', isset( \IPS\Request::i()->ref ) ? \IPS\Request::i()->ref : NULL ), \IPS\Login::LOGIN_REAUTHENTICATE );
$login = new LoginClass( Url::internal( 'app=core&module=system&controller=login&do=link', 'front', 'login' )->setQueryString( 'ref', isset( Request::i()->ref ) ? Request::i()->ref : NULL ), LoginClass::LOGIN_REAUTHENTICATE );
$login->reauthenticateAs = $member;
$error = NULL;
/* If successful (or if there's no way to reauthenticate which would only happen if a login handler has been deleted)) complete the link... */
try
{
if ( $success = $login->authenticate() or ( !\count( $login->usernamePasswordMethods() ) and !\count( $login->buttonMethods() ) ) )
if ( $success = $login->authenticate() or ( !count( $login->usernamePasswordMethods() ) and !count( $login->buttonMethods() ) ) )
{
$handler->completeLink( $member, $details['details'] );
unset( $_SESSION['linkAccounts'] );
$destination = \IPS\Request::i()->referrer( FALSE, TRUE ) ?: \IPS\Http\Url::internal( '' );
$destination = Request::i()->referrer( FALSE, TRUE ) ?: Url::internal( '' );
$success = new \IPS\Login\Success( $member, $handler );
$success = new Success( $member, $handler );
if ( $success->mfa() )
{
$_SESSION['processing2FA'] = array( 'memberId' => $success->member->member_id, 'anonymous' => $success->anonymous, 'remember' => $success->rememberMe, 'destination' => (string) $destination, 'handler' => $success->handler->id );
\IPS\Output::i()->redirect( $destination->setQueryString( '_mfaLogin', 1 ) );
Output::i()->redirect( $destination->setQueryString( '_mfaLogin', 1 ) );
}
$success->process();
\IPS\Output::i()->redirect( $destination->setQueryString( '_fromLogin', 1 ) );
Output::i()->redirect( $destination->setQueryString( '_fromLogin', 1 ) );
}
}
catch ( \IPS\Login\Exception $e )
catch ( Exception $e )
{
$error = $e->getMessage();
}
/* Otherwise show the reauthenticate form */
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::setCacheTime( false );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('login');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->mergeSocialAccount( $handler, $member, $login, $error );
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->pageCaching = FALSE;
Output::i()->title = Member::loggedIn()->language()->addToStack('login');
Output::i()->output = Theme::i()->getTemplate( 'system' )->mergeSocialAccount( $handler, $member, $login, $error );
}
/**
@@ -260,40 +292,40 @@ class _login extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function logout()
protected function logout() : void
{
$member = \IPS\Member::loggedIn();
$member = Member::loggedIn();
/* CSRF Check */
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Work out where we will be going after log out */
if( !empty( $_SERVER['HTTP_REFERER'] ) )
{
$referrer = \IPS\Http\Url::createFromString( $_SERVER['HTTP_REFERER'] );
$redirectUrl = ( $referrer instanceof \IPS\Http\Url\Internal and ( !isset( $referrer->queryString['do'] ) or $referrer->queryString['do'] != 'validating' ) ) ? $referrer : \IPS\Http\Url::internal('');
$referrer = Url::createFromString( $_SERVER['HTTP_REFERER'] );
$redirectUrl = ( $referrer instanceof Internal and ( !isset( $referrer->queryString['do'] ) or $referrer->queryString['do'] != 'validating' ) ) ? $referrer : Url::internal('');
}
else
{
$redirectUrl = \IPS\Http\Url::internal( '' );
$redirectUrl = Url::internal( '' );
}
/* Are we logging out back to an admin user? */
if( isset( $_SESSION['logged_in_as_key'] ) )
{
$key = $_SESSION['logged_in_as_key'];
unset( \IPS\Data\Store::i()->$key );
unset( Store::i()->$key );
unset( $_SESSION['logged_in_as_key'] );
unset( $_SESSION['logged_in_from'] );
\IPS\Output::i()->redirect( $redirectUrl );
Output::i()->redirect( $redirectUrl );
}
/* Do it */
\IPS\Login::logout( $redirectUrl );
LoginClass::logout( $redirectUrl );
/* Redirect */
\IPS\Output::i()->redirect( $redirectUrl->setQueryString( '_fromLogout', 1 ) );
Output::i()->redirect( $redirectUrl->setQueryString( '_fromLogout', 1 ) );
}
/**
@@ -301,33 +333,33 @@ class _login extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function loginas()
protected function loginas() : void
{
if ( !\IPS\Request::i()->key or !\IPS\Login::compareHashes( (string) \IPS\Data\Store::i()->admin_login_as_user, (string) \IPS\Request::i()->key ) )
if ( !Request::i()->key or ! LoginClass::compareHashes( (string) Store::i()->admin_login_as_user, (string) Request::i()->key ) )
{
\IPS\Output::i()->error( 'invalid_login_as_user_key', '3S167/1', 403, '' );
Output::i()->error( 'invalid_login_as_user_key', '3S167/1', 403, '' );
}
/* Load member and admin user */
$member = \IPS\Member::load( \IPS\Request::i()->id );
$admin = \IPS\Member::load( \IPS\Request::i()->admin );
$member = Member::load( Request::i()->id );
$admin = Member::load( Request::i()->admin );
/* Not logged in as admin? */
if ( $admin->member_id != \IPS\Member::loggedIn()->member_id )
if ( $admin->member_id != Member::loggedIn()->member_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->addRef( (string) \IPS\Request::i()->url() )->setQueryString( '_err', 'login_as_user_login' ) );
Output::i()->redirect( Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->addRef( (string) Request::i()->url() )->setQueryString( '_err', 'login_as_user_login' ) );
}
/* Do it */
$_SESSION['logged_in_from'] = array( 'id' => $admin->member_id, 'name' => $admin->name );
$unique_id = \IPS\Login::generateRandomString();
$unique_id = LoginClass::generateRandomString();
$_SESSION['logged_in_as_key'] = $unique_id;
\IPS\Data\Store::i()->$unique_id = $member->member_id;
Store::i()->$unique_id = $member->member_id;
/* Ditch the key */
unset( \IPS\Data\Store::i()->admin_login_as_user );
unset( Store::i()->admin_login_as_user );
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
Output::i()->redirect( Url::internal( '' ) );
}
}
@@ -10,43 +10,67 @@
namespace IPS\core\modules\front\system;
use IPS\Text\Encrypt;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\DateTime;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Email;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Captcha;
use IPS\Helpers\Form\Email as FormEmail;
use IPS\Helpers\Form\Password;
use IPS\Helpers\Form\Text;
use IPS\Http\Url;
use IPS\Login;
use IPS\Login\Handler;
use IPS\Login\Handler\Standard;
use IPS\Login\Success;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Text\Encrypt;
use IPS\Theme;
use UnderflowException;
use function defined;
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;
}
/**
* Lost Password
*/
class _lostpass extends \IPS\Dispatcher\Controller
class lostpass extends Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
public bool $isContentPage = FALSE;
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
if ( \IPS\Settings::i()->allow_forgot_password == 'disabled' )
if ( Settings::i()->allow_forgot_password == 'disabled' )
{
\IPS\Output::i()->error( 'page_doesnt_exist', '2S151/2', 404, '' );
Output::i()->error( 'page_doesnt_exist', '2S151/2', 404, '' );
}
if ( \IPS\Settings::i()->allow_forgot_password == 'redirect' )
if ( Settings::i()->allow_forgot_password == 'redirect' )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::external( \IPS\Settings::i()->allow_forgot_password_target ) );
Output::i()->redirect( Url::external( Settings::i()->allow_forgot_password_target ) );
}
return parent::execute();
parent::execute();
}
/**
@@ -54,54 +78,55 @@ class _lostpass extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Build the form */
$form = new \IPS\Helpers\Form( "lostpass", 'request_password' );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
$form = new Form( "lostpass", 'request_password' );
$form->add( new FormEmail( 'email_address', NULL, TRUE, array( 'bypassProfanity' => Text::BYPASS_PROFANITY_ALL ) ) );
$captcha = new \IPS\Helpers\Form\Captcha;
$captcha = new Captcha;
if ( (string) $captcha !== '' )
{
$form->add( $captcha );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('lost_password');
Output::i()->title = Member::loggedIn()->language()->addToStack('lost_password');
/* Handle the reset */
if ( $values = $form->values() )
{
if( !\IPS\Login::emailIsInUse( $values['email_address'] ) )
if( !Login::emailIsInUse( $values['email_address'] ) )
{
/* We intentionally show the same message as if the request was successful to avoid leaking information about membership */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->lostPassConfirm( 'lost_pass_confirm' );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->lostPassConfirm( 'lost_pass_confirm' );
return;
}
/* If using "normal" method and we have an account, and at least one login handler we can process a password change for, we're good */
$member = \IPS\Member::load( $values['email_address'], 'email' );
if ( $member->member_id and \IPS\Settings::i()->allow_forgot_password == 'normal' )
$member = Member::load( $values['email_address'], 'email' );
if ( $member->member_id and Settings::i()->allow_forgot_password == 'normal' )
{
foreach ( \IPS\Login::methods() as $method )
foreach ( Login::methods() as $method )
{
if ( $method->canChangePassword( $member ) )
{
return $this->_sendForgotPasswordEmail( $member );
$this->_sendForgotPasswordEmail( $member );
return;
}
}
}
/* If not, send them to the handler if we can */
foreach( \IPS\Login::methods() as $method )
foreach( Login::methods() as $method )
{
if( $method->emailIsInUse( $values['email_address'] ) === TRUE )
{
if ( $url = $method->forgotPasswordUrl() )
{
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
}
}
}
@@ -109,60 +134,62 @@ class _lostpass extends \IPS\Dispatcher\Controller
/* If we have no way to reset the password, can we allow creating a local password as a last attempt? */
if ( $member->member_id )
{
foreach( \IPS\Login::methods() as $method )
foreach( Login::methods() as $method )
{
if ( $method instanceof \IPS\Login\Handler\Standard )
if ( $method instanceof Standard )
{
return $this->_sendForgotPasswordEmail( $member );
$this->_sendForgotPasswordEmail( $member );
return;
}
}
}
/* Otherwise, sorry, we can't do this */
\IPS\Output::i()->error( 'lost_pass_not_possible', '1S151/3', 403, '' );
Output::i()->error( 'lost_pass_not_possible', '1S151/3', 403, '' );
}
/* Show form */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->lostPass( $form );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->lostPass( $form );
}
/**
* Send forgot password email
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @return void
*/
protected function _sendForgotPasswordEmail( \IPS\Member $member )
protected function _sendForgotPasswordEmail( Member $member ) : void
{
/* If we have an existing validation record, we can just reuse it */
$sendEmail = TRUE;
/* Delete any lost pass validating records that are older than 45 minutes - These records are only valid for one hour. */
\IPS\Db::i()->delete( 'core_validating', [ 'member_id=? AND lost_pass=1 AND entry_date<?', $member->member_id, time() - 2700 ] );
Db::i()->delete( 'core_validating', [ 'member_id=? AND lost_pass=1 AND entry_date<?', $member->member_id, time() - 2700 ] );
try
{
$existing = \IPS\Db::i()->select( array( 'vid', 'email_sent' ), 'core_validating', array( 'member_id=? AND lost_pass=1', $member->member_id ) )->first();
$existing = Db::i()->select( array( 'vid', 'email_sent' ), 'core_validating', array( 'member_id=? AND lost_pass=1', $member->member_id ) )->first();
$vid = $existing['vid'];
/* If we sent a lost password email within the last 15 minutes, don't send another one otherwise someone could be a nuisence */
if ( $existing['email_sent'] and $existing['email_sent'] > ( time() - 900 ) )
{
$plainSecurityKey = $existing['security_key'];
$sendEmail = FALSE;
}
else
{
$plainSecurityKey = \IPS\Login::generateRandomString();
\IPS\Db::i()->update( 'core_validating', [ 'email_sent' => time(), 'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag() ], [ 'vid=?', $vid ] );
$plainSecurityKey = Login::generateRandomString();
Db::i()->update( 'core_validating', [ 'email_sent' => time(), 'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag() ], [ 'vid=?', $vid ] );
}
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
$vid = md5( $member->members_pass_hash . \IPS\Login::generateRandomString() );
$plainSecurityKey = \IPS\Login::generateRandomString();
\IPS\Db::i()->insert( 'core_validating', [
$vid = md5( $member->members_pass_hash . Login::generateRandomString() );
$plainSecurityKey = Login::generateRandomString();
Db::i()->insert( 'core_validating', array(
'vid' => $vid,
'member_id' => $member->member_id,
'entry_date' => time(),
@@ -170,13 +197,13 @@ class _lostpass extends \IPS\Dispatcher\Controller
'ip_address' => $member->ip_address,
'email_sent' => time(),
'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag()
] );
) );
}
/* Send email */
if ( $sendEmail )
{
\IPS\Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $vid, $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $vid, $plainSecurityKey ), Email::TYPE_TRANSACTIONAL )->send( $member );
$message = "lost_pass_confirm";
}
else
@@ -185,9 +212,9 @@ class _lostpass extends \IPS\Dispatcher\Controller
}
/* Show confirmation page with further instructions */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->lostPassConfirm( $message );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->lostPassConfirm( $message );
}
/**
@@ -195,45 +222,45 @@ class _lostpass extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function validate()
protected function validate() : void
{
/* Prevent the vid key from being exposed in referrers */
\IPS\Output::i()->sendHeader( "Referrer-Policy: origin" );
Output::i()->sendHeader( "Referrer-Policy: origin" );
try
{
$record = \IPS\Db::i()->select( '*', 'core_validating', array( 'vid=? AND member_id=? AND lost_pass=1', \IPS\Request::i()->vid, \IPS\Request::i()->mid ) )->first();
$record = Db::i()->select( '*', 'core_validating', array( 'vid=? AND member_id=? AND lost_pass=1', Request::i()->vid, Request::i()->mid ) )->first();
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
\IPS\Output::i()->error( 'no_validation_key', '2S151/1', 410, '' );
Output::i()->error( 'no_validation_key', '2S151/1', 410, '' );
}
/* Check security key */
if( !\IPS\Login::compareHashes( Encrypt::fromTag( $record['security_key'] )->decrypt(), \IPS\Request::i()->security_key ) )
/* Check security key is valid. */
if( !Login::compareHashes( Encrypt::fromTag( $record['security_key'] )->decrypt(), Request::i()->security_key ) )
{
\IPS\Output::i()->error( 'lostpass_invalid_security_key', '2S151/5', 403, '' );
Output::i()->error( 'lostpass_invalid_security_key', '2S151/5', 403, '' );
}
/* Show a nicer error message if their link has expired */
if( $record['entry_date'] < \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() )
if( $record['entry_date'] < DateTime::create()->sub( new DateInterval( 'PT1H' ) )->getTimestamp() )
{
\IPS\Output::i()->error( 'lost_pass_expired', '2S151/4', 410, '' );
Output::i()->error( 'lost_pass_expired', '2S151/4', 410, '' );
}
/* Show form for new password */
$form = new \IPS\Helpers\Form( "resetpass", 'save' );
$form->add( new \IPS\Helpers\Form\Password( 'password', NULL, TRUE, array( 'protect' => TRUE, 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'checkStrength' => TRUE, 'strengthMember' => \IPS\Member::load( \IPS\Request::i()->mid ) ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', NULL, TRUE, array( 'protect' => TRUE, 'confirm' => 'password' ) ) );
$form = new Form( "resetpass", 'save' );
$form->add( new Password( 'password', NULL, TRUE, array( 'protect' => TRUE, 'showMeter' => Settings::i()->password_strength_meter, 'checkStrength' => TRUE, 'strengthMember' => Member::load( Request::i()->mid ) ) ) );
$form->add( new Password( 'password_confirm', NULL, TRUE, array( 'protect' => TRUE, 'confirm' => 'password' ) ) );
/* Set new password */
if ( $values = $form->values() )
{
/* Get the member */
$member = \IPS\Member::load( $record['member_id'] );
$member = Member::load( $record['member_id'] );
/* Reset the failed logins storage - we don't need to save because the login handler will do that for us later */
\IPS\Db::i()->delete( 'core_login_failures', [ 'login_member_id=?', $member->member_id ] );
Db::i()->delete( 'core_login_failures', [ 'login_member_id=?', $member->member_id ] );
$member->failed_login_count = 0;
/* Now reset the member's password. If no handlers accept the change, create a local password */
@@ -243,24 +270,24 @@ class _lostpass extends \IPS\Dispatcher\Controller
$member->save();
}
$member->invalidateSessionsAndLogins( \IPS\Session::i()->id );
$member->invalidateSessionsAndLogins( Session::i()->id );
/* Delete validating record and log in */
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=? AND lost_pass=1', $member->member_id ) );
Db::i()->delete( 'core_validating', array( 'member_id=? AND lost_pass=1', $member->member_id ) );
$success = new \IPS\Login\Success( $member, \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' ) );
$success = new Success( $member, Handler::findMethod( 'IPS\Login\Handler\Standard' ) );
if ( $success->mfa() )
{
$_SESSION['processing2FA'] = array( 'memberId' => $success->member->member_id, 'anonymous' => $success->anonymous, 'remember' => $success->rememberMe, 'destination' => (string) \IPS\Http\Url::internal( '' ), 'handler' => $success->handler->id );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' )->setQueryString( '_mfaLogin', 1 ) );
$_SESSION['processing2FA'] = array( 'memberId' => $success->member->member_id, 'anonymous' => $success->anonymous, 'remember' => $success->rememberMe, 'destination' => (string) Url::internal( '' ), 'handler' => $success->handler->id );
Output::i()->redirect( Url::internal( '' )->setQueryString( '_mfaLogin', 1 ) );
}
$success->process();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' )->setQueryString( '_fromLogin', 1 ) );
Output::i()->redirect( Url::internal( '' )->setQueryString( '_fromLogin', 1 ) );
}
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->resetPass( $form );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'lost_password' );
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->resetPass( $form );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'lost_password' );
}
}
@@ -11,46 +11,55 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use function defined;
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;
}
/**
* Mark site as read
*/
class _markread extends \IPS\Dispatcher\Controller
class markread extends Controller
{
/**
* Mark site as read
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
\IPS\Member::loggedIn()->markAllAsRead();
Member::loggedIn()->markAllAsRead();
}
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
Output::i()->json( 'OK' );
}
else
{
/* Don't redirect to an external domain unless explicitly requested, and don't redirect back to ACP */
$redirectTo = \IPS\Request::i()->referrer( FALSE, FALSE, 'front' ) ?: \IPS\Http\Url::internal( '' );
$redirectTo = Request::i()->referrer( FALSE, FALSE, 'front' ) ?: Url::internal( '' );
if ( $redirectTo === NULL )
{
$redirectTo = \IPS\Http\Url::internal( '' );
$redirectTo = Url::internal( '' );
}
\IPS\Output::i()->redirect( $redirectTo, 'core_site_marked_as_read' );
Output::i()->redirect( $redirectTo, 'core_site_marked_as_read' );
}
}
}
@@ -11,29 +11,47 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Data\Store;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use XMLWriter;
use function count;
use function defined;
use function is_array;
use function json_encode;
use function time;
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;
}
/**
* Live meta tag editor
*/
class _metatags extends \IPS\Dispatcher\Controller
class metatags extends Controller
{
/**
* Redirect the request appropriately
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$this->_checkPermissions();
$_SESSION['live_meta_tags'] = TRUE;
\IPS\Output::i()->redirect( \IPS\Http\Url::external( \IPS\Settings::i()->base_url ) );
Output::i()->redirect( Url::external( Settings::i()->base_url ) );
}
/**
@@ -41,33 +59,33 @@ class _metatags extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function save()
protected function save() : void
{
/* Check permissions and CSRF */
$this->_checkPermissions();
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Delete any existing database entries, as we are about to re-insert */
\IPS\Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', \IPS\Request::i()->meta_url ) );
\IPS\Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', trim( \IPS\Request::i()->meta_url, '/' ) ) );
Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', Request::i()->meta_url ) );
Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', trim( Request::i()->meta_url, '/' ) ) );
/* Start save array */
$save = array(
'meta_url' => \IPS\Request::i()->meta_url,
'meta_title' => \IPS\Request::i()->meta_tag_title,
'meta_url' => Request::i()->meta_url,
'meta_title' => Request::i()->meta_tag_title,
);
$_tags = array();
$metaTagNames = \IPS\Request::i()->meta_tag_name;
$metaTagCustomNames = \IPS\Request::i()->meta_tag_name_other;
$metaTagValues = \IPS\Request::i()->meta_tag_content;
$metaTagNames = Request::i()->meta_tag_name;
$metaTagCustomNames = Request::i()->meta_tag_name_other;
$metaTagValues = Request::i()->meta_tag_content;
/* Remove any default meta tags that have not been edited - don't save them permanently */
if( isset( \IPS\Request::i()->defaultMetaTag ) )
if( isset( Request::i()->defaultMetaTag ) )
{
foreach( \IPS\Request::i()->defaultMetaTag as $k => $v )
foreach( Request::i()->defaultMetaTag as $k => $v )
{
if( ( $key = array_search( $k, $metaTagNames ) ) !== FALSE AND $metaTagValues[ $key ] == $v )
{
@@ -81,9 +99,9 @@ class _metatags extends \IPS\Dispatcher\Controller
}
/* If we asked to remove a default meta tag, store specially so we can do so */
if( isset( \IPS\Request::i()->deleteDefaultMeta ) )
if( isset( Request::i()->deleteDefaultMeta ) )
{
foreach( \IPS\Request::i()->deleteDefaultMeta as $v )
foreach( Request::i()->deleteDefaultMeta as $v )
{
$_tags[ $v ] = NULL;
@@ -99,7 +117,7 @@ class _metatags extends \IPS\Dispatcher\Controller
}
/* Store the new meta tags */
if( \is_array( $metaTagNames ) )
if( is_array( $metaTagNames ) )
{
foreach( $metaTagNames as $k => $v )
{
@@ -111,22 +129,22 @@ class _metatags extends \IPS\Dispatcher\Controller
}
/* Save the meta tags, if there are any to save */
if( \count( $_tags ) OR \IPS\Request::i()->meta_tag_title != '' )
if( count( $_tags ) OR Request::i()->meta_tag_title != '' )
{
$save['meta_tags'] = json_encode( $_tags );
\IPS\Db::i()->insert( 'core_seo_meta', $save );
Db::i()->insert( 'core_seo_meta', $save );
}
unset( \IPS\Data\Store::i()->metaTags );
unset( Store::i()->metaTags );
/* Send back to the page */
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
return;
}
\IPS\Output::i()->redirect( \IPS\Http\Url::external( \IPS\Settings::i()->base_url . \IPS\Request::i()->url ) );
Output::i()->redirect( Url::external( Settings::i()->base_url . Request::i()->url ) );
}
/**
@@ -134,13 +152,13 @@ class _metatags extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function end()
protected function end() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
$_SESSION['live_meta_tags'] = FALSE;
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( \IPS\Request::i()->url ) );
Output::i()->redirect( Request::i()->referrer() ?: Url::internal( '' ) );
}
/**
@@ -148,16 +166,16 @@ class _metatags extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function _checkPermissions()
protected function _checkPermissions() : void
{
if( !\IPS\Member::loggedIn()->member_id OR !\IPS\Member::loggedIn()->isAdmin() )
if( !Member::loggedIn()->member_id OR !Member::loggedIn()->isAdmin() )
{
\IPS\Output::i()->error( 'meta_editor_no_admin', '2C155/1', 403, '' );
Output::i()->error( 'meta_editor_no_admin', '2C155/1', 403, '' );
}
if( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_manage' ) )
if( !Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_manage' ) )
{
\IPS\Output::i()->error( 'meta_editor_no_acpperm', '3C155/2', 403, '' );
Output::i()->error( 'meta_editor_no_acpperm', '3C155/2', 403, '' );
}
}
@@ -166,25 +184,30 @@ class _metatags extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manifest()
protected function manifest() : void
{
if( \IPS\IN_DEV === FALSE AND isset( \IPS\Data\Store::i()->manifest ) )
if( \IPS\IN_DEV === FALSE AND isset( Store::i()->manifest ) )
{
$output = \IPS\Data\Store::i()->manifest;
$output = Store::i()->manifest;
}
else
{
$manifest = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
$manifest = json_decode( Settings::i()->manifest_details, TRUE );
if( !$manifest )
{
$manifest = [];
}
if ( ! isset( $manifest['cache_key'] ) )
{
$manifest['cache_key'] = time();
Settings::i()->changeValues( array( 'manifest_details' => json_encode( $manifest ) ) );
}
$output = array(
'scope' => rtrim( \IPS\Settings::i()->base_url, '/' ) . '/',
'name' => \IPS\Settings::i()->board_name,
'theme_color' => \IPS\Theme::i()->settings['header']
'scope' => rtrim( Settings::i()->base_url, '/' ) . '/',
'name' => Settings::i()->board_name
);
foreach( $manifest as $k => $v )
@@ -195,8 +218,8 @@ class _metatags extends \IPS\Dispatcher\Controller
}
}
$homeScreen = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
$homeScreenMaskable = json_decode( \IPS\Settings::i()->icons_homescreen_maskable, TRUE ) ?? array();
$homeScreen = json_decode( Settings::i()->icons_homescreen, TRUE ) ?? array();
$homeScreenMaskable = json_decode( Settings::i()->icons_homescreen_maskable, TRUE ) ?? array();
foreach( $homeScreen as $k => $v )
{
@@ -207,14 +230,14 @@ class _metatags extends \IPS\Dispatcher\Controller
$output['icons'] = array();
}
$file = \IPS\File::get( 'core_Icons', $v['url'] );
$file = File::get( 'core_Icons', $v['url'] );
$output['icons'][] = array(
'src' => (string) $file->url->setQueryString( 'v', $manifest['cache_key'] ),
'type' => \IPS\File::getMimeType( $file->originalFilename ),
'type' => File::getMimeType( $file->originalFilename ),
'sizes' => $v['width'] . 'x' . $v['height'],
'purpose' => 'any'
);;
);
}
}
@@ -227,23 +250,23 @@ class _metatags extends \IPS\Dispatcher\Controller
$output['icons'] = array();
}
$file = \IPS\File::get( 'core_Icons', $v['url'] );
$file = File::get( 'core_Icons', $v['url'] );
$output['icons'][] = array(
'src' => (string) $file->url->setQueryString( 'v', $manifest['cache_key'] ),
'type' => \IPS\File::getMimeType( $file->originalFilename ),
'type' => File::getMimeType( $file->originalFilename ),
'sizes' => $v['width'] . 'x' . $v['height'],
'purpose' => 'maskable'
);;
);
}
}
\IPS\Data\Store::i()->manifest = $output;
Store::i()->manifest = $output;
}
$cacheHeaders = ( \IPS\IN_DEV !== true AND \IPS\Theme::designersModeEnabled() !== true ) ? \IPS\Output::getCacheHeaders( time(), 86400 ) : array();
$cacheHeaders = ( !\IPS\IN_DEV ) ? Output::getCacheHeaders( time(), 86400 ) : array();
\IPS\Output::i()->sendOutput( json_encode( $output, JSON_PRETTY_PRINT ), 200, 'application/manifest+json', $cacheHeaders );
Output::i()->sendOutput( json_encode( $output, JSON_PRETTY_PRINT ), 200, 'application/manifest+json', $cacheHeaders );
}
/**
@@ -251,18 +274,18 @@ class _metatags extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function iebrowserconfig()
protected function iebrowserconfig() : void
{
if( \IPS\IN_DEV === FALSE AND isset( \IPS\Data\Store::i()->iebrowserconfig ) )
if( \IPS\IN_DEV === FALSE AND isset( Store::i()->iebrowserconfig ) )
{
$output = \IPS\Data\Store::i()->iebrowserconfig;
$output = Store::i()->iebrowserconfig;
}
else
{
$manifest = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
$manifest = json_decode( Settings::i()->manifest_details, TRUE );
/* Init */
$xml = new \XMLWriter;
$xml = new XMLWriter;
$xml->openMemory();
$xml->setIndent( TRUE );
$xml->startDocument( '1.0', 'UTF-8' );
@@ -275,13 +298,13 @@ class _metatags extends \IPS\Dispatcher\Controller
$xml->writeElement( 'TileColor', $manifest['theme_color'] );
}
$homeScreen = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
$homeScreen = json_decode( Settings::i()->icons_homescreen, TRUE ) ?? array();
foreach( $homeScreen as $k => $v )
{
if( mb_strpos( $k, 'msapplication' ) !== FALSE AND $k !== 'msapplication-TileImage' )
{
$file = \IPS\File::get( 'core_Icons', $v['url'] );
$file = File::get( 'core_Icons', $v['url'] );
$xml->startElement( str_replace( 'msapplication-', '', $k ) );
$xml->startAttribute( 'src' );
@@ -298,11 +321,11 @@ class _metatags extends \IPS\Dispatcher\Controller
$output = $xml->outputMemory();
\IPS\Data\Store::i()->iebrowserconfig = $output;
Store::i()->iebrowserconfig = $output;
}
$cacheHeaders = ( \IPS\IN_DEV !== true AND \IPS\Theme::designersModeEnabled() !== true ) ? \IPS\Output::getCacheHeaders( time(), 86400 ) : array();
$cacheHeaders = ( !\IPS\IN_DEV ) ? Output::getCacheHeaders( time(), 86400 ) : array();
\IPS\Output::i()->sendOutput( $output, 200, 'application/xml', $cacheHeaders );
Output::i()->sendOutput( $output, 200, 'application/xml', $cacheHeaders );
}
}
@@ -11,41 +11,52 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
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;
}
/**
* Moderation
*/
class _moderation extends \IPS\Dispatcher\Controller
class moderation extends Controller
{
/**
* Flag Member As Spammer
*
* @return void
*/
protected function flagAsSpammer()
protected function flagAsSpammer() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
$redirectTarget = ( \IPS\Request::i()->referrer ) ? \IPS\Http\Url::createFromString( urldecode( \IPS\Request::i()->referrer ), TRUE ) : $member->url();
$redirectTarget = ( Request::i()->referrer ) ? Url::createFromString( urldecode( Request::i()->referrer ) ) : $member->url();
if ( $member->member_id and $member->member_id != \IPS\Member::loggedIn()->member_id and \IPS\Member::loggedIn()->modPermission('can_flag_as_spammer') and !$member->modPermission() and !$member->isAdmin() )
if ( $member->member_id and $member->member_id != Member::loggedIn()->member_id and Member::loggedIn()->modPermission('can_flag_as_spammer') and !$member->modPermission() and !$member->isAdmin() )
{
if ( \IPS\Request::i()->s )
if ( Request::i()->s )
{
$member->flagAsSpammer();
\IPS\Session::i()->modLog( 'modlog__spammer_flagged', array( $member->name => FALSE ) );
Session::i()->modLog( 'modlog__spammer_flagged', array( $member->name => FALSE ) );
$actions = explode( ',', \IPS\Settings::i()->spm_option );
$actions = explode( ',', Settings::i()->spm_option );
/* Redirect to the users profile if we're deleting the content to avoid that the moderator sees a 404 error message (unless soft delete is enabled) */
if ( \in_array( 'delete', $actions ) AND !( \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') AND \IPS\Settings::i()->dellog_retention_period ) )
if ( in_array( 'delete', $actions ) AND !( Member::loggedIn()->modPermission('can_manage_deleted_content') AND Settings::i()->dellog_retention_period ) )
{
$redirectTarget = $member->url();
}
@@ -53,10 +64,10 @@ class _moderation extends \IPS\Dispatcher\Controller
else
{
$member->unflagAsSpammer();
\IPS\Session::i()->modLog( 'modlog__spammer_unflagged', array( $member->name => FALSE ) );
Session::i()->modLog( 'modlog__spammer_unflagged', array( $member->name => FALSE ) );
}
}
\IPS\Output::i()->redirect( $redirectTarget, ( \IPS\Request::i()->s ) ? 'account_flagged' : 'account_unflagged');
Output::i()->redirect( $redirectTarget, ( Request::i()->s ) ? 'account_flagged' : 'account_unflagged');
}
}
File diff suppressed because it is too large. Load diff
@@ -11,25 +11,31 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Output;
use IPS\Theme;
use function defined;
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;
}
/**
* Offline page Controller
*/
class _offline extends \IPS\Dispatcher\Controller
class offline extends Controller
{
/**
* View Notifications
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->offline(), 200, 'text/html' );
Output::i()->metaTags['robots'] = 'noindex';
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core', 'global' )->offline() );
}
}
@@ -11,29 +11,43 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use Exception;
use IPS\Data\Cache;
use IPS\DateTime;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use IPS\Theme;
use OutOfRangeException;
use function defined;
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;
}
/**
* Editor AJAX functions Controller
*/
class _pixabay extends \IPS\Dispatcher\Controller
class pixabay extends Controller
{
/**
* Show the dialog window
*
* @return void
* @throws \Exception
* @throws Exception
*/
protected function manage()
protected function manage() : void
{
if ( \IPS\Settings::i()->pixabay_enabled )
if ( Settings::i()->pixabay_enabled )
{
$output = \IPS\Theme::i()->getTemplate( 'system' )->pixabay( \IPS\Request::i()->uploader );
\IPS\Output::i()->sendOutput( $output );
$output = Theme::i()->getTemplate( 'system' )->pixabay( Request::i()->uploader );
Output::i()->sendOutput( $output );
}
}
@@ -41,23 +55,23 @@ class _pixabay extends \IPS\Dispatcher\Controller
* Search pixabay
*
* @return void
* @throws \Exception
* @throws Exception
*/
protected function search()
protected function search() : void
{
if ( \IPS\Settings::i()->pixabay_enabled )
if ( Settings::i()->pixabay_enabled )
{
$limit = isset( \IPS\Request::i()->limit ) ? \IPS\Request::i()->limit : 20;
$offset = isset( \IPS\Request::i()->offset ) ? \IPS\Request::i()->offset : 0;
$query = isset( \IPS\Request::i()->search ) ? \IPS\Request::i()->search : '';
$url = \IPS\Http\Url::external( "https://pixabay.com/api/" );
$limit = isset( Request::i()->limit ) ? Request::i()->limit : 20;
$offset = isset( Request::i()->offset ) ? Request::i()->offset : 0;
$query = isset( Request::i()->search ) ? Request::i()->search : '';
$url = Url::external( "https://pixabay.com/api/" );
$parameters = array(
'key' => \IPS\Settings::i()->pixabay_apikey,
'key' => Settings::i()->pixabay_apikey,
'image_type' => 'photo',
'per_page' => 20,
'page' => ( $offset ) ? ceil( $offset / 20 ) + 1 : 1,
'safesearch' => ( \IPS\Settings::i()->pixabay_safesearch ) ? 'true' : 'false',
'safesearch' => ( Settings::i()->pixabay_safesearch ) ? 'true' : 'false',
);
$parameters['q'] = urlencode( $query );
@@ -65,19 +79,19 @@ class _pixabay extends \IPS\Dispatcher\Controller
try
{
$request = \IPS\Data\Cache::i()->getWithExpire( $cacheKey, TRUE );
$request = Cache::i()->getWithExpire( $cacheKey, TRUE );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$url = $url->setQueryString($parameters);
$request = json_decode( $url->request()->get()->content, true );
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $request, \IPS\DateTime::create()->add( new \DateInterval('P1D') ), TRUE );
Cache::i()->storeWithExpire( $cacheKey, $request, DateTime::create()->add( new DateInterval('P1D') ), TRUE );
}
if ( isset( $request['message'] ) AND $request['message'] )
{
\IPS\Output::i()->json( array('error' => $request['message'] ) );
Output::i()->json( array('error' => $request['message'] ) );
}
@@ -88,15 +102,19 @@ class _pixabay extends \IPS\Dispatcher\Controller
'thumb' => $row['webformatURL'],
'url' => $row['largeImageURL'],
'imgid' => $row['id'],
'imageHeight' => $row['imageHeight'],
'imageWidth' => $row['imageWidth'],
'thumbHeight' => $row['webformatHeight'],
'thumbWidth' => $row['webformatWidth'],
);
}
if ( empty( $results['images'] ) )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ) );
Output::i()->json( array( 'error' => Theme::i()->getTemplate( 'system', 'core' )->noResults() ) );
}
\IPS\Output::i()->json( $results );
Output::i()->json( $results );
}
}
@@ -105,14 +123,14 @@ class _pixabay extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function getById()
protected function getById() : void
{
if ( isset( \IPS\Request::i()->id ) )
if ( isset( Request::i()->id ) )
{
$url = \IPS\Http\Url::external( "https://pixabay.com/api/" )->setQueryString( array(
'key' => \IPS\Settings::i()->pixabay_apikey,
'id' => \IPS\Request::i()->id
$url = Url::external( "https://pixabay.com/api/" )->setQueryString( array(
'key' => Settings::i()->pixabay_apikey,
'id' => Request::i()->id
) );
@@ -131,7 +149,7 @@ class _pixabay extends \IPS\Dispatcher\Controller
}
/* Now get the URL contents */
$data = \IPS\Http\Url::external( $url )->request()->get();
$data = Url::external( $url )->request()->get();
if ( ! $filename )
{
@@ -139,7 +157,7 @@ class _pixabay extends \IPS\Dispatcher\Controller
}
list( $image, $type ) = explode( '/', $data->httpHeaders['Content-Type'] );
\IPS\Output::i()->json( array( 'content' => base64_encode( $data->content ), 'type' => $data->httpHeaders['Content-Type'], 'imageType' => $type, 'filename' => $filename ) );
Output::i()->json( array( 'content' => base64_encode( $data->content ), 'type' => $data->httpHeaders['Content-Type'], 'imageType' => $type, 'filename' => $filename ) );
}
}
}
@@ -1,33 +0,0 @@
<?php
/**
* @brief A deliberately empty controller intended for use by plugins which need to add small actions
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 09 Aug 2013
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* A deliberately empty controller intended for use by plugins which need to add small actions
*/
class _plugins extends \IPS\Dispatcher\Controller
{
/**
* This is only here to prevent a fatal error calling to manage from the dispatcher
*
* @return void
*/
protected function manage()
{
}
}
+19 -10
View File
@@ -11,38 +11,47 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Output;
use IPS\Poll as PollClass;
use IPS\Request;
use IPS\Theme;
use OutOfRangeException;
use function defined;
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;
}
/**
* Poll View Voters Controller
*/
class _poll extends \IPS\Dispatcher\Controller
class poll extends Controller
{
/**
* View log
*
* @return void
*/
protected function voters()
protected function voters() : void
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
Output::i()->metaTags['robots'] = 'noindex';
try
{
$poll = \IPS\Poll::load( \IPS\Request::i()->id );
$poll = PollClass::load( Request::i()->id );
if ( !$poll->canSeeVoters() )
{
\IPS\Output::i()->error( 'node_error', '2C174/2', 403, '' );
Output::i()->error( 'node_error', '2C174/2', 403, '' );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pollVoters( $poll->getVotes( \IPS\Request::i()->question, \IPS\Request::i()->option ) );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core', 'global' )->pollVoters( $poll->getVotes( Request::i()->question, Request::i()->option ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C174/1', 404, '' );
Output::i()->error( 'node_error', '2C174/1', 404, '' );
}
}
}
@@ -11,58 +11,69 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use function defined;
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;
}
/**
* Privacy Policy
*/
class _privacy extends \IPS\Dispatcher\Controller
class privacy extends Controller
{
/**
* Privacy Policy
*
* @return void
*/
protected function manage()
protected function manage() : void
{
if ( \IPS\Settings::i()->privacy_type == "none" )
if ( Settings::i()->privacy_type == "none" )
{
\IPS\Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_set_to_none_acp' );
Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_set_to_none_acp' );
}
if ( \IPS\Settings::i()->privacy_type == "external" )
if ( Settings::i()->privacy_type == "external" )
{
if ( $url = \IPS\Settings::i()->privacy_link )
if ( $url = Settings::i()->privacy_link )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $url ) );
Output::i()->redirect( Url::external( $url ) );
}
else
{
\IPS\Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_link_not_set_acp' );
Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_link_not_set_acp' );
}
}
$subprocessors = array();
/* Work out the main subprocessors that the user has no direct choice over */
if ( \IPS\Settings::i()->privacy_show_processors )
if ( Settings::i()->privacy_show_processors )
{
foreach( \IPS\Application::enabledApplications() as $app )
foreach( Application::enabledApplications() as $app )
{
$subprocessors = array_merge( $subprocessors, $app->privacyPolicyThirdParties() );
}
}
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=privacy', NULL, 'privacy' ), array(), 'loc_viewing_privacy_policy' );
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=privacy', NULL, 'privacy' ), array(), 'loc_viewing_privacy_policy' );
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('privacy') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('privacy');
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->privacy( $subprocessors );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack('privacy') );
Output::i()->title = Member::loggedIn()->language()->addToStack('privacy');
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->privacy( $subprocessors );
}
}
@@ -11,77 +11,93 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use IPS\core\Advertisement;
use IPS\DateTime;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use OutOfRangeException;
use UnderflowException;
use function defined;
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;
}
/**
* Redirect
*/
class _redirect extends \IPS\Dispatcher\Controller
class redirect extends Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
public bool $isContentPage = FALSE;
/**
* Handle munged links
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* The URL may have had a line-break added in the outbound email if it's a long URL, we need to fix that. Then check the key matches. */
try
{
$url = str_replace( [ '%20','%0D' ], '', (string) \IPS\Http\Url::createFromString( \IPS\Request::i()->url ) );
if ( \IPS\Login::compareHashes( hash_hmac( "sha256", $url, \IPS\Settings::i()->site_secret_key ), (string) \IPS\Request::i()->key ) OR \IPS\Login::compareHashes( hash_hmac( "sha256", $url, \IPS\Settings::i()->site_secret_key . 'r' ), (string) \IPS\Request::i()->key ) )
$url = str_replace( [ '%20','%0D' ], '', (string) Url::createFromString( Request::i()->url ) );
if ( Login::compareHashes( hash_hmac( "sha256", $url, Settings::i()->site_secret_key ), (string) Request::i()->key ) OR Login::compareHashes( hash_hmac( "sha256", $url, Settings::i()->site_secret_key . 'r' ), (string) Request::i()->key ) )
{
/* Construct the URL */
$url = \IPS\Http\Url::external( $url );
$url = Url::external( $url );
/* If this is coming from email tracking, log the click */
if( isset( \IPS\Request::i()->email ) AND \IPS\Settings::i()->prune_log_emailstats != 0 )
if( isset( Request::i()->email ) AND Settings::i()->prune_log_emailstats != 0 )
{
/* If we have a row for "today" then update it, otherwise insert one */
$today = \IPS\DateTime::create()->format( 'Y-m-d' );
$today = DateTime::create()->format( 'Y-m-d' );
try
{
/* We only include the time column in the query so that the db index can be effectively used */
if( !\IPS\Request::i()->type )
if( !Request::i()->type )
{
$currentRow = \IPS\Db::i()->select( '*', 'core_statistics', array( 'type=? AND time>? AND value_4=? AND extra_data IS NULL', 'email_clicks', 1, $today ) )->first();
$currentRow = Db::i()->select( '*', 'core_statistics', array( 'type=? AND time>? AND value_4=? AND extra_data IS NULL', 'email_clicks', 1, $today ) )->first();
}
else
{
$currentRow = \IPS\Db::i()->select( '*', 'core_statistics', array( 'type=? AND time>? AND value_4=? AND extra_data=?', 'email_clicks', 1, $today, \IPS\Request::i()->type ) )->first();
$currentRow = Db::i()->select( '*', 'core_statistics', array( 'type=? AND time>? AND value_4=? AND extra_data=?', 'email_clicks', 1, $today, Request::i()->type ) )->first();
}
\IPS\Db::i()->update( 'core_statistics', "value_1=value_1+1", array( 'id=?', $currentRow['id'] ) );
Db::i()->update( 'core_statistics', "value_1=value_1+1", array( 'id=?', $currentRow['id'] ) );
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Db::i()->insert( 'core_statistics', array( 'type' => 'email_clicks', 'value_1' => 1, 'value_4' => $today, 'time' => time(), 'extra_data' => \IPS\Request::i()->type ) );
Db::i()->insert( 'core_statistics', array( 'type' => 'email_clicks', 'value_1' => 1, 'value_4' => $today, 'time' => time(), 'extra_data' => Request::i()->type ) );
}
}
/* Send the user to the URL after setting the referrer policy header */
\IPS\Output::i()->sendHeader( "Referrer-Policy: origin" );
\IPS\Output::i()->redirect( $url, \IPS\Request::i()->email ? '' : \IPS\Member::loggedIn()->language()->addToStack('external_redirect'), 303, \IPS\Request::i()->email ? FALSE : TRUE );
Output::i()->sendHeader( "Referrer-Policy: origin" );
Output::i()->redirect( $url, Request::i()->email ? '' : Member::loggedIn()->language()->addToStack('external_redirect'), 303, !Request::i()->email );
}
/* If it doesn't validate, send the user to the index page */
else
{
throw new \DomainException();
throw new DomainException();
}
}
catch( \DomainException $e )
catch( DomainException $e )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
Output::i()->redirect( Url::internal('') );
}
}
@@ -90,34 +106,34 @@ class _redirect extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function advertisement()
protected function advertisement() : void
{
/* Get the advertisement */
$advertisement = array();
if( isset( \IPS\Request::i()->ad ) )
if( isset( Request::i()->ad ) )
{
try
{
$advertisement = \IPS\core\Advertisement::load( \IPS\Request::i()->ad );
$advertisement = Advertisement::load( Request::i()->ad );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'ad_not_found', '2C159/2', 404, 'ad_not_found_admin' );
Output::i()->error( 'ad_not_found', '2C159/2', 404, 'ad_not_found_admin' );
}
}
if( !$advertisement->id OR !$advertisement->link )
{
\IPS\Output::i()->error( 'ad_not_found', '2C159/1', 404, 'ad_not_found_admin' );
Output::i()->error( 'ad_not_found', '2C159/1', 404, 'ad_not_found_admin' );
}
if ( \IPS\Login::compareHashes( hash_hmac( "sha256", $advertisement->link, \IPS\Settings::i()->site_secret_key ), (string) \IPS\Request::i()->key ) OR \IPS\Login::compareHashes( hash_hmac( "sha256", $advertisement->link, \IPS\Settings::i()->site_secret_key . 'a' ), (string) \IPS\Request::i()->key ) )
if ( Login::compareHashes( hash_hmac( "sha256", $advertisement->link, Settings::i()->site_secret_key ), (string) Request::i()->key ) OR Login::compareHashes( hash_hmac( "sha256", $advertisement->link, Settings::i()->site_secret_key . 'a' ), (string) Request::i()->key ) )
{
/* We need to update click count for this advertisement. Does it need to be shut off too due to hitting click maximum?
Note that this needs to be done as a string to do "col=col+1", which is why we're not using the ActiveRecord save() method.
Updating by doing col=col+1 is more reliable when there are several clicks at nearly the same time. */
$update = "ad_clicks=ad_clicks+1";
$update = "ad_clicks=ad_clicks+1, ad_daily_clicks=ad_daily_clicks+1";
if( $advertisement->maximum_unit == 'c' AND $advertisement->maximum_value > -1 AND $advertisement->clicks + 1 >= $advertisement->maximum_value )
{
@@ -125,15 +141,15 @@ class _redirect extends \IPS\Dispatcher\Controller
}
/* Update the database */
\IPS\Db::i()->update( 'core_advertisements', $update, array( 'ad_id=?', $advertisement->id ) );
Db::i()->update( 'core_advertisements', $update, array( 'ad_id=?', $advertisement->id ) );
/* And do the redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $advertisement->link ) );
Output::i()->redirect( Url::external( $advertisement->link ) );
}
/* If it doesn't validate, send the user to the index page */
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
Output::i()->redirect( Url::internal('') );
}
}
}
@@ -12,9 +12,22 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\DateTime;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Http\Url\Exception;
use IPS\Http\Url\Internal;
use IPS\Output;
use IPS\Request;
use IPS\Settings;
use function defined;
use function intval;
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;
}
@@ -23,33 +36,33 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*
* Deprecated controller maintained for backwards compatibility with old referral links
*/
class _referrals extends \IPS\Dispatcher\Controller
class referrals extends Controller
{
/**
* Handle Referral
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Request::i()->setCookie( 'referred_by', \intval( \IPS\Request::i()->id ), \IPS\DateTime::create()->add( new \DateInterval( 'P1Y' ) ) );
Request::i()->setCookie( 'referred_by', intval( Request::i()->id ), DateTime::create()->add( new DateInterval( 'P1Y' ) ) );
try
{
$target = \IPS\Request::i()->direct ? \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->direct ) ) : \IPS\Http\Url::baseUrl();
$target = Request::i()->direct ? Url::createFromString( base64_decode( Request::i()->direct ) ) : Url::baseUrl();
}
catch( \IPS\Http\Url\Exception $e )
catch( Exception $e )
{
$target = NULL;
}
if ( $target instanceof \IPS\Http\Url\Internal )
if ( $target instanceof Internal )
{
\IPS\Output::i()->redirect( $target );
Output::i()->redirect( $target );
}
else
{
\IPS\Output::i()->redirect( \IPS\Settings::i()->base_url );
Output::i()->redirect( Settings::i()->base_url );
}
}
}
File diff suppressed because it is too large. Load diff
@@ -12,96 +12,82 @@ namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
use function mb_strlen;
use const LIBXML_NOWARNING;
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Settings;
use IPS\Theme;
use function count;
use function defined;
use const IPS\DEBUG_JS;
use const IPS\DEV_DEBUG_JS;
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' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Service worker controller
*/
class _serviceworker extends \IPS\Dispatcher\Controller
class serviceworker extends Controller
{
/**
* View Notifications
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$cachedUrls = array();
$cachedUrls[] = (string) Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
$notificationIcon = NULL;
/* Get an icon to use in notifications */
$homeScreenIcons = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
$homeScreenIcons = json_decode( Settings::i()->icons_homescreen, TRUE ) ?? array();
if( \count( $homeScreenIcons ) )
if( count( $homeScreenIcons ) )
{
foreach( $homeScreenIcons as $name => $image )
{
if( isset( $image['width'] ) and $image['width'] == 192 )
{
$notificationIcon = \IPS\File::get( 'core_Icons', $image['url'] )->url;
$notificationIcon = File::get( 'core_Icons', $image['url'] )->url;
break;
}
}
}
/* VARIABLES TO PASS THROUGH TO JS */
$CACHE_VERSION = \IPS\Theme::i()->cssCacheBustKey();
$variables = [
"DEBUG" => boolval( ( \IPS\IN_DEV and \IPS\DEV_DEBUG_JS ) or \IPS\DEBUG_JS ),
"BASE_URL" => \IPS\Settings::i()->base_url,
"CACHED_ASSETS" => $cachedUrls,
"CACHE_NAME" => "invision-community-{$CACHE_VERSION}",
"CACHE_VERSION" => $CACHE_VERSION,
"NOTIFICATION_ICON" => $notificationIcon ?: null,
"DEFAULT_NOTIFICATION_TITLE" => \IPS\Member::loggedIn()->language()->addToStack('default_notification_title'),
"DEFAULT_NOTIFICATION_BODY" => \IPS\Member::loggedIn()->language()->addToStack('default_notification_body'),
"OFFLINE_PAGE" => $this->buildCollapsedOfflinePage(),
];
$DEBUG = ( ( \IPS\IN_DEV and DEV_DEBUG_JS ) or DEBUG_JS ) ? 'true' : 'false'; // Weird casting is intentional.
$BASE_URL = Settings::i()->base_url;
$CACHED_ASSETS = json_encode( $cachedUrls, JSON_UNESCAPED_SLASHES );
$OFFLINE_URL = (string) Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
$CACHE_VERSION = Theme::i()->cssCacheBustKey();
$NOTIFICATION_ICON = $notificationIcon ? "\"{$notificationIcon}\"" : 'null'; // Weird casting is intentional. 'null' will become literal null in JS file.
$DEFAULT_NOTIFICATION_TITLE = Member::loggedIn()->language()->addToStack('default_notification_title');
$DEFAULT_NOTIFICATION_BODY = Member::loggedIn()->language()->addToStack('default_notification_body');
$variables["OFFLINE_PAGE_SIZE"] = \strlen( $variables["OFFLINE_PAGE"] );
$output = "\"use strict;\"\n\n";
foreach ( $variables as $var => $value )
{
$toEncode = json_encode( $value, JSON_UNESCAPED_SLASHES );
$output .= <<<JAVASCRIPT
const {$var} = {$toEncode};
$output = <<<JAVASCRIPT
"use strict";
const DEBUG = {$DEBUG};
const BASE_URL = "{$BASE_URL}";
const CACHED_ASSETS = {$CACHED_ASSETS};
const CACHE_NAME = 'invision-community-{$CACHE_VERSION}';
const OFFLINE_URL = "{$OFFLINE_URL}";
const NOTIFICATION_ICON = {$NOTIFICATION_ICON};
const DEFAULT_NOTIFICATION_TITLE = "{$DEFAULT_NOTIFICATION_TITLE}";
const DEFAULT_NOTIFICATION_BODY = "{$DEFAULT_NOTIFICATION_BODY}";
JAVASCRIPT;
}
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
Member::loggedIn()->language()->parseOutputForDisplay( $output );
$output .= file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/js/serviceWorker.js' );
$cacheHeaders = \IPS\IN_DEV !== true ? \IPS\Output::getCacheHeaders(time(), 86400) : array();
\IPS\Output::i()->sendOutput($output, 200, 'text/javascript', $cacheHeaders);
}
/**
* Return template for offline page
*
* @return string
*/
protected function buildCollapsedOfflinePage() : string
{
$html = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->swOffline();
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $html );
$doc = new \IPS\Xml\DOMDocument( "2.0", "utf-8" );
$doc->preserveWhiteSpace = false;
$doc->loadHTML( $html, LIBXML_NOBLANKS );
$doc->formatOutput = true;
$compact = $doc->saveHTML();
// We don't need these segments of whitespace. HTML entities can be used if it's absolutely necessary
return preg_replace( "/\s+/", " ", $compact );
$cacheHeaders = !\IPS\IN_DEV ? Output::getCacheHeaders(time(), 86400) : array();
Output::i()->sendOutput($output, 200, 'text/javascript', $cacheHeaders);
}
}
File diff suppressed because it is too large. Load diff
@@ -11,38 +11,46 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application\Module;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use function defined;
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;
}
/**
* Splash Controller
*/
class _splash extends \IPS\Dispatcher\Controller
class splash extends Controller
{
/**
* Splash
*
* @return void
*/
protected function manage()
protected function manage() : void
{
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
if ( \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members', 'front' ) ) )
if ( Member::loggedIn()->canAccessModule( Module::get( 'core', 'members', 'front' ) ) )
{
\IPS\Output::i()->redirect( \IPS\Member::loggedIn()->url() );
Output::i()->redirect( Member::loggedIn()->url() );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings', 'front', 'settings' ) );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=settings', 'front', 'settings' ) );
}
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
Output::i()->redirect( Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) );
}
}
}
@@ -11,30 +11,42 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Http\Url\Exception;
use IPS\Http\Url\Internal;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use function defined;
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;
}
/**
* Terms of Use
*/
class _terms extends \IPS\Dispatcher\Controller
class terms extends Controller
{
/**
* Terms of Use
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=terms', NULL, 'terms' ), array(), 'loc_viewing_reg_terms' );
Session::i()->setLocation( Url::internal( 'app=core&module=system&controller=terms', NULL, 'terms' ), array(), 'loc_viewing_reg_terms' );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_terms');
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->terms();
Output::i()->title = Member::loggedIn()->language()->addToStack('reg_terms');
Output::i()->sidebar['enabled'] = FALSE;
Output::i()->bodyClasses[] = 'ipsLayout_minimal';
Output::i()->output = Theme::i()->getTemplate( 'system' )->terms();
}
/**
@@ -42,37 +54,37 @@ class _terms extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function dismiss()
protected function dismiss() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
\IPS\Request::i()->setCookie( 'guestTermsDismissed', 1, NULL, FALSE );
Request::i()->setCookie( 'guestTermsDismissed', 1, NULL, FALSE );
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'message' => \IPS\Member::loggedIn()->language()->addToStack( 'terms_dismissed' ) ) );
Output::i()->json( array( 'message' => Member::loggedIn()->language()->addToStack( 'terms_dismissed' ) ) );
}
else
{
if ( isset( \IPS\Request::i()->ref ) )
if ( isset( Request::i()->ref ) )
{
try
{
$url = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
$url = Url::createFromString( base64_decode( Request::i()->ref ) );
}
catch( \IPS\Http\Url\Exception $e )
catch( Exception $e )
{
$url = NULL;
}
if ( $url instanceof \IPS\Http\Url\Internal and !$url->openRedirect() )
if ( $url instanceof Internal and !$url->openRedirect() )
{
\IPS\Output::i()->redirect( $url, 'terms_dismissed' );
Output::i()->redirect( $url, 'terms_dismissed' );
}
}
/* Still here? Just redirect to the index */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ), 'terms_dismissed' );
Output::i()->redirect( Url::internal( '' ), 'terms_dismissed' );
}
}
}
@@ -11,23 +11,32 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use function defined;
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;
}
/**
* Theme Changer
*/
class _theme extends \IPS\Dispatcher\Controller
class theme extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
parent::execute();
}
@@ -37,32 +46,20 @@ class _theme extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
if( \IPS\Member::loggedIn()->member_id )
if( Member::loggedIn()->member_id )
{
\IPS\Member::loggedIn()->skin = (int) \IPS\Request::i()->id;
\IPS\Member::loggedIn()->save();
Member::loggedIn()->skin = (int) Request::i()->id;
Member::loggedIn()->save();
}
else
{
\IPS\Request::i()->setCookie( 'theme', (int) \IPS\Request::i()->id );
Request::i()->setCookie( 'theme', (int) Request::i()->id );
}
/* Make sure VSE cookie is killed */
if ( isset( \IPS\Request::i()->cookie['vseThemeId'] ) )
{
\IPS\Request::i()->setCookie( 'vseThemeId', 0 );
}
if ( \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] )
{
\IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] = 0;
\IPS\Member::loggedIn()->save();
}
\IPS\Output::i()->redirect( \IPS\Request::i()->referrer() ?: \IPS\Http\Url::internal( '' ) );
Output::i()->redirect( Request::i()->referrer() ?: Url::internal( '' ) );
}
}
@@ -0,0 +1,296 @@
<?php
/**
* @brief Theme Editor
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 3 July 2023
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
use Exception;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\Helpers\Form\Color;
use IPS\Http\Url;
use IPS\Image;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Theme;
use IPS\Settings;
use RuntimeException;
use function defined;
use function file_get_contents;
use function getimagesize;
use function in_array;
use function json_encode;
use function time;
use const IMAGETYPE_GIF;
use const IMAGETYPE_JPEG;
use const IMAGETYPE_PNG;
use const IMAGETYPE_WEBP;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Theme editor
*/
class themeeditor extends Controller
{
/**
* Shows the theme editor
*
* @return void
* @throws Exception
*/
public function manage(): void
{
if ( ! Member::loggedIn()->isAdmin() ) /* @todo add perm checks in Admin CP */
{
Output::i()->error( 'no_permission', '2S164/1', 403, '' );
}
/* CSS */
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'framework/framework.css', 'core', 'front' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/theme_editor.css', 'core', 'front' ) );
/* JS */
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'library.js' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'js/jslang.php?langId=' . Member::loggedIn()->language()->id, 'core', 'interface' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'framework.js' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
Color::loadJS();
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'themeeditor/themeEditor.js', 'core', 'interface' ) );
/* Flag our theme as being edited */
Theme::i()->editingStart();
Output::i()->output = Theme::i()->getTemplate( 'themeeditor', 'core', 'front' )->themeEditorTemplate();
Output::i()->sendOutput( Output::i()->output );
}
/**
* Save the theme editor
*
* @return void
* @throws Exception
*/
public function save(): void
{
/* Has someone ended this session for us? */
if ( ! Member::loggedIn()->isEditingTheme() )
{
Output::i()->error( 'theme_editor_session_expired', '2S164/A', 403, '' );
}
/* Useful for debugging */
$changed = Theme::i()->getCssVariables( Theme::CUSTOM_ONLY );
$data = [];
$unchanged = [];
$rootCssVariables = Theme::master()->getCssVariables( Theme::FORCE_DEFAULT );
$cssVariables = [];
foreach( $rootCssVariables as $key => $value )
{
$cssVariables[ $key ] = $value;
if ( isset( Request::i()->$key ) )
{
$newValue = Request::i()->$key;
/* We have a value from the theme editor, so override the root value */
$cssVariables[ $key ] = $newValue;
if ( $newValue !== $value )
{
$changed[ $key ] = $newValue;
}
else
{
/* If we previously changed the value and we changed it back, clear it */
if( array_key_exists( $key, $changed ) )
{
unset( $changed[ $key ] );
}
$unchanged[ $key ] = $value;
}
}
}
/* Save the css */
Theme::i()->setCssVariables( $changed );
/* Work out the positioning based on a 3x3 grid */
$positioning = [];
foreach( [ 'logo', 'navigation', 'user', 'breadcrumb', 'search' ] as $type )
{
$key = 'set__i-position-' . $type;
if ( isset( Request::i()->$key ) )
{
$positioning[ $type ] = Request::i()->$key;
}
}
$data['set_theme_editor_data']['header'] = $positioning;
/* Delete or save the logo(s) */
foreach( [ 'logo-light', 'logo-dark', 'mobile-logo-light', 'mobile-logo-dark' ] as $type )
{
$logoKey = '';
switch ( $type )
{
case 'logo-light':
$logoKey = 'front';
break;
case 'logo-dark':
$logoKey = 'front-dark';
break;
case 'mobile-logo-light':
$logoKey = 'mobile';
break;
case 'mobile-logo-dark':
$logoKey = 'mobile-dark';
break;
}
if ( isset( Request::i()->{ 'delete__set__'. $type } ) )
{
File::get( 'core_Theme', Theme::i()->logo[ $logoKey ]['url'] )->delete();
$data['logo'][$logoKey] = [];
}
else
{
if ( isset( $_FILES['set__' . $type] ) and $_FILES['set__' . $type]['tmp_name'] )
{
$file = $_FILES['set__' . $type];
$fileExt = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
$imageAttributes = getimagesize( $file['tmp_name'] );
$logoHeightSetting = 'set__i-logo--he';
if ( $fileExt === 'svg' or in_array( $imageAttributes[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP] ) )
{
/* So we can use the File::create() obfuscation, but it will not re-add the file extension back on for SVG, so... */
$baseName = pathinfo( $file['name'], PATHINFO_FILENAME );
$extension = pathinfo( $file['name'], PATHINFO_EXTENSION );
$randomKey = substr(bin2hex( random_bytes(3) ), 0, 6); // Produces a 6-character long key
$uniqueFilename = "{$baseName}_{$randomKey}.{$extension}";
try
{
if ( $fileExt !== 'svg' )
{
$image = Image::create( file_get_contents( $file['tmp_name'] ), false );
$width = $image->width;
$height = $image->height;
}
else
{
$image = file_get_contents( $file['tmp_name'] );
$width = null;
$height = null;
try
{
/* I tried many fancy methods including XML parsing but they all failed, but regex will always work becaue it is the best thing in the world */
preg_match( '#<image(?:.+?)width="(\d+)"\s+height="(\d+)"#i', $image, $matches );
if ( isset( $matches[1] ) and isset( $matches[2] ) )
{
$width = $matches[1];
$height = $matches[2];
}
}
catch ( Exception $e ) { }
}
$file = File::create( 'core_Theme', $uniqueFilename, $image, null, true, null, false );
$data['logo'][$logoKey] = [
'url' => (string)$file,
'setting_height' => isset( Request::i()->$logoHeightSetting ) ? Request::i()->$logoHeightSetting : 100,
'img_width' => $width,
'img_height' => $height,
];
}
catch ( RuntimeException $e )
{
$data['logo'][$logoKey] = [];
}
}
}
}
}
Theme::i()->saveSet( $data );
/* And now layout options */
$currentOptions = json_decode( Theme::i()->view_options, true );
if ( ! is_array( $currentOptions ) )
{
$currentOptions = [];
}
foreach( Request::i() as $key => $value )
{
if( str_starts_with( $key, 'layout_' ) )
{
$option = substr( $key, strlen( 'layout_' ) );
$currentOptions[ $option ] = $value;
}
}
Theme::i()->view_options = json_encode( $currentOptions );
/* Custom CSS */
if ( isset( Request::i()->set__customCSS ) )
{
Theme::i()->custom_css = Request::i()->set__customCSS;
}
Theme::i()->save();
/* Save the history - potential 'roll back' feature? */
Db::i()->insert( 'core_theme_editor_history', [
'member_id' => Member::loggedIn()->member_id,
'time' => time(),
'set_id' => Theme::i()->_id,
'json' => json_encode( [
'changed' => array_keys( $changed ),
'unchanged' => $unchanged,
'newCssVariables' => $cssVariables,
'currentCssVariables' => Theme::i()->getCssVariables(),
'logos' => $data,
'request' => Request::i()->request
] )
] );
Output::i()->redirect( Url::internal( "" ), ( \IPS\IN_DEV and ! Settings::i()->theme_designer_mode ) ? 'theme_editor_saved_indev' : 'theme_editor_saved' );
}
/**
* Close the theme editor
*
* @return void
* @throws Exception
*/
public function close(): void
{
/* Flag our theme as no longer being edited */
Theme::i()->editingFinish();
Output::i()->redirect( Url::internal( "" ) );
}
}
@@ -1,94 +0,0 @@
<?php
/**
* @brief Twitter Authentication
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 24 February 2017
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Twitter auth for ACP
*/
class _twitter extends \IPS\Dispatcher\Controller
{
/**
* View Announcement
*
* @return void
*/
protected function manage()
{
if ( ! \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->addRef( \IPS\Http\Url::internal( 'app=core&module=system&controller=twitter' ) ) );
}
/* Check the member has permission to manage promote settings */
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'promote_manage' ) )
{
\IPS\Output::i()->error( 'no_module_permission', '3C375/2', 403, '' );
}
$twitter = \IPS\Login\Handler::findMethod('IPS\Login\Handler\Oauth1\Twitter');
/* Required handler present and enabled? */
if( $twitter === NULL )
{
\IPS\Output::i()->error( 'promote_twitter_setup_app', '3C375/3', 403, '' );
}
$promote = \IPS\core\Promote::getPromoter( 'Twitter' );
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=twitter' );
$callback = $twitter->redirectionEndpoint()->setQueryString( 'state', $twitter->id . '-' . base64_encode( $url ) . '-' . \IPS\Session::i()->csrfKey . '-' );
try
{
/* Get a request token */
if ( !isset( \IPS\Request::i()->oauth_token ) )
{
$response = $twitter->requestToken( (string) $callback );
\IPS\Output::i()->redirect( "https://api.twitter.com/oauth/authorize?force_login=1&oauth_token={$response['oauth_token']}" );
}
/* CSRF Check */
if ( \IPS\Request::i()->csrfKey !== \IPS\Session::i()->csrfKey )
{
throw new \IPS\Login\Exception( 'CSRF_FAIL', \IPS\Login\Exception::INTERNAL_ERROR );
}
/* Authenticate */
$response = $twitter->exchangeToken( \IPS\Request::i()->oauth_verifier, \IPS\Request::i()->oauth_token );
/* Store the settings */
$promote->saveSettings( array(
'id' => $response['user_id'],
'owner' => \IPS\Member::loggedIn()->member_id,
'secret' => $response['oauth_token_secret'],
'token' => $response['oauth_token'],
'name' => $response['screen_name']
) );
/* Show a done page */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('promote_twitter_sorted');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promote' )->promoteTwitterComplete( $response );
}
catch ( \Exception $e )
{
\IPS\Log::log( $e, 'twitter_promote' );
\IPS\Output::i()->error( 'generic_error', '4C375/1', 403, $e->getMessage() );
}
}
}
@@ -11,50 +11,84 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Login;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Theme;
use UnderflowException;
use function defined;
use function implode;
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;
}
/**
* Unsubscribe
*/
class _unsubscribe extends \IPS\Dispatcher\Controller
class unsubscribe extends Controller
{
/**
* Unsubscribe the user
*
* @return void
*/
protected function manage()
protected function manage() : void
{
/* Prevent the email and secure key from being exposed in referrers */
\IPS\Output::i()->sendHeader( "Referrer-Policy: origin" );
Output::i()->sendHeader( "Referrer-Policy: origin" );
/* Get the member being requested */
if( empty( \IPS\Request::i()->email ) )
if( empty( Request::i()->email ) )
{
\IPS\Output::i()->error( 'no_user_to_unsubscribe', '2S127/3', 404, '' );
Output::i()->error( 'no_user_to_unsubscribe', '2S127/3', 404, '' );
}
$member = \IPS\Member::load( \IPS\Request::i()->email, 'email' );
$member = Member::load( Request::i()->email, 'email' );
if( !$member->member_id )
{
\IPS\Output::i()->error( 'no_user_to_unsubscribe', '2S127/2', 404, '' );
Output::i()->error( 'no_user_to_unsubscribe', '2S127/2', 404, '' );
}
/* Verify the key is correct */
if ( \IPS\Login::compareHashes( md5( $member->email . ':' . $member->members_pass_hash ), (string) \IPS\Request::i()->key ) )
if ( Login::compareHashes( md5( $member->email . ':' . $member->members_pass_hash ), (string) Request::i()->key ) )
{
$action = 'bulkEmail';
if ( isset( \IPS\Request::i()->action ) and \IPS\Request::i()->action === 'markSolved' )
if ( isset( Request::i()->action ) and Request::i()->action === 'markSolved' )
{
$action = 'markSolved';
$member->members_bitoptions['no_solved_reenage'] = 1;
$member->save();
}
elseif ( isset( Request::i()->action ) and Request::i()->action === 'expertNudge' )
{
$action = 'expertNudge';
/* Update the core_notification_preferences entry to remove email */
try
{
$pref = Db::i()->select( '*', 'core_notification_preferences', array( "member_id=? and notification_key=?", $member->member_id, 'new_topics_to_review' ) )->first();
$methods = explode( ',', $pref['preference'] );
$methods = array_diff( $methods, array( 'email' ) );
Db::i()->update( 'core_notification_preferences', array( 'preference' => implode( ',', $methods ) ), array( "member_id=? and notification_key=?", $member->member_id, 'new_topics_to_review' ) );
}
catch( UnderflowException )
{
Db::i()->insert( 'core_notification_preferences', array(
'member_id' => $member->member_id,
'notification_key' => 'new_topics_to_review',
'preference' => 'inline,push'
), TRUE );
}
}
else
{
/* Set the member not to receive future emails */
@@ -66,14 +100,14 @@ class _unsubscribe extends \IPS\Dispatcher\Controller
}
/* And then show them a confirmation screen */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->unsubscribed( $action );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('unsubscribed');
Output::i()->output = Theme::i()->getTemplate( 'system' )->unsubscribed( $action );
Output::i()->title = Member::loggedIn()->language()->addToStack('unsubscribed');
}
else
{
/* Key did not match */
\IPS\Output::i()->error( 'no_user_to_unsubscribe', '3S127/4', 403, '' );
Output::i()->error( 'no_user_to_unsubscribe', '3S127/4', 403, '' );
}
}
}
+43 -29
View File
@@ -11,29 +11,43 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use Exception;
use IPS\Application;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Lang;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Widget;
use UnderflowException;
use function defined;
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;
}
/**
* Visual Language Editor
*/
class _vle extends \IPS\Dispatcher\Controller
class vle extends Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'languages', 'lang_words' ) )
if ( !Member::loggedIn()->hasAcpRestriction( 'core', 'languages', 'lang_words' ) )
{
\IPS\Output::i()->json( 'NO_PERMISSION', 403 );
Output::i()->json( 'NO_PERMISSION', 403 );
}
return parent::execute();
parent::execute();
}
/**
@@ -41,18 +55,18 @@ class _vle extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function get()
public function get() : void
{
try
{
$word = \IPS\Member::loggedIn()->language()->get( \IPS\Request::i()->key );
$word = Member::loggedIn()->language()->get( Request::i()->key );
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
$word = NULL;
}
\IPS\Output::i()->json( $word );
Output::i()->json( $word );
}
/**
@@ -60,24 +74,24 @@ class _vle extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function set()
public function set() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
try
{
$word = \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=? AND word_key=?', \IPS\Member::loggedIn()->language()->id, \IPS\Request::i()->key ) )->first();
$word = Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=? AND word_key=?', Member::loggedIn()->language()->id, Request::i()->key ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$word = NULL;
}
try
{
$application = \IPS\Application::load( $word['word_app'] );
$application = Application::load( $word['word_app'] );
}
catch ( \Exception $e )
catch ( Exception $e )
{
return;
}
@@ -86,28 +100,28 @@ class _vle extends \IPS\Dispatcher\Controller
{
if ( $word['word_export'] )
{
\IPS\Db::i()->update( 'core_sys_lang_words', array( 'word_custom' => \IPS\Request::i()->value, 'word_custom_version' => $application->long_version ), array( 'word_id=?', $word['word_id'] ) );
Db::i()->update( 'core_sys_lang_words', array( 'word_custom' => Request::i()->value, 'word_custom_version' => $application->long_version ), array( 'word_id=?', $word['word_id'] ) );
}
else
{
\IPS\Lang::saveCustom( $word['word_app'], $word['word_key'], array( \IPS\Member::loggedIn()->language()->id => \IPS\Request::i()->value ) );
Lang::saveCustom( $word['word_app'], $word['word_key'], array( Member::loggedIn()->language()->id => Request::i()->value ) );
}
$string = \IPS\Request::i()->value ?: $word['word_default'];
$string = Request::i()->value ?: $word['word_default'];
}
else
{
\IPS\Lang::saveCustom( 'core', \IPS\Request::i()->key, array( \IPS\Member::loggedIn()->language()->id => \IPS\Request::i()->value ) );
Lang::saveCustom( 'core', Request::i()->key, array( Member::loggedIn()->language()->id => Request::i()->value ) );
$string = \IPS\Request::i()->value;
$string = Request::i()->value;
}
\IPS\Db::i()->insert( 'core_admin_logs', array(
'member_id' => \IPS\Member::loggedIn()->member_id,
'member_name' => \IPS\Member::loggedIn()->name,
Db::i()->insert( 'core_admin_logs', array(
'member_id' => Member::loggedIn()->member_id,
'member_name' => Member::loggedIn()->name,
'ctime' => time(),
'note' => json_encode( array( $word['word_key'] => FALSE, \IPS\Member::loggedIn()->language()->title => FALSE ) ),
'ip_address' => \IPS\Request::i()->ipAddress(),
'note' => json_encode( array( $word['word_key'] => FALSE, Member::loggedIn()->language()->title => FALSE ) ),
'ip_address' => Request::i()->ipAddress(),
'appcomponent' => 'core',
'module' => 'system',
'controller' => 'vle',
@@ -115,8 +129,8 @@ class _vle extends \IPS\Dispatcher\Controller
'lang_key' => 'acplogs__lang_translate'
) );
\IPS\Widget::deleteCaches();
Widget::deleteCaches();
\IPS\Output::i()->sendOutput( $string, 200, 'text/text' );
Output::i()->sendOutput( $string, 200, 'text/text' );
}
}
@@ -1,250 +0,0 @@
<?php
/**
* @brief Visual Theme Editor
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 7 Nov 2013
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Visual Language Editor
*/
class _vse extends \IPS\Dispatcher\Controller
{
/**
* Shows the VSE
*
* @return void
*/
public function show()
{
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_easy_editor' ) OR ! \IPS\Member::loggedIn()->isAdmin() OR ! \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] )
{
\IPS\Output::i()->error( 'core_theme_cant_easy_mode', '2S164/1', 403, '' );
}
$theme = null;
if ( isset( \IPS\Request::i()->cookie['vseThemeId'] ) and \IPS\Request::i()->cookie['vseThemeId'] )
{
try
{
$theme = \IPS\Theme::load( \intval( \IPS\Request::i()->cookie['vseThemeId'] ) );
if ( ! $theme->by_skin_gen )
{
$theme = null;
}
}
catch( \OutOfRangeException $ex )
{
$theme = null;
}
}
if ( $theme === null )
{
\IPS\Output::i()->error( 'core_theme_cant_easy_mode', '2S164/2', 403, '' );
}
/* CSS */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'framework.css', 'core', 'front' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/vse.css', 'core', 'front' ) );
/* JS */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'library.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'js/jslang.php?langId=' . \IPS\Member::loggedIn()->language()->id, 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'framework.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'vse/vsedata.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_vse.js', 'core', 'front' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
$css = $theme->getRawCss( 'core', 'front', 'custom', \IPS\Theme::RETURN_ALL, true );
$customCss = null;
if ( isset( $css['core']['front']['custom']['custom.css'] ) )
{
/* Custom CSS */
$functionName = "css_" . mt_rand();
\IPS\Theme::makeProcessFunction( \IPS\Theme::fixResourceTags( $css['core']['front']['custom']['custom.css']['css_content'], 'front' ), $functionName, '', FALSE, TRUE );
$themeFunction = 'IPS\\Theme\\'. $functionName;
$customCss = $themeFunction();
}
$form = new \IPS\Helpers\Form( 'form', NULL );
$settings = $theme->getThemeSettings();
$settingKeyValues = array();
$colorValues = array();
foreach( $settings as $key => $data )
{
$settingKeyValues[ $key ] = $data['_value'];
if( $data['sc_type'] === 'Color' )
{
if( preg_match( '/^#?[0-9a-fA-F]{6}$/', $data['_value'] ) )
{
$colorValue = str_replace('#', '', $data['_value']);
$rgb = array();
if( \strlen( $colorValue ) === 3 )
{
$rgb[] = hexdec( \substr( $colorValue, 0, 1 ) . \substr( $colorValue, 0, 1 ) );
$rgb[] = hexdec( \substr( $colorValue, 1, 1 ) . \substr( $colorValue, 1, 1 ) );
$rgb[] = hexdec( \substr( $colorValue, 2, 1 ) . \substr( $colorValue, 2, 1 ) );
}
else
{
$rgb[] = hexdec( \substr( $colorValue, 0, 2 ) );
$rgb[] = hexdec( \substr( $colorValue, 2, 2 ) );
$rgb[] = hexdec( \substr( $colorValue, 4, 2 ) );
}
$colorValues[ $key ] = implode( ', ', $rgb );
}
}
if ( $data['sc_show_in_vse'] )
{
$value = ( isset( $data['sv_value'] ) ) ? $data['sv_value'] : $data['sc_default'];
$data['sc_type'] = ( empty( $data['sc_type'] ) ) ? 'Text' : $data['sc_type'];
$class = '\IPS\Helpers\Form\\' . $data['sc_type'];
$options = array();
switch ( $data['sc_type'] )
{
case 'Text':
case 'TextArea':
case 'Select':
$options['sc_multiple'] = $data['sc_multiple'];
// No break
case 'Radio':
$content = json_decode( $data['sc_content'], TRUE );
$options = array();
if( \is_array( $content ) )
{
foreach( $content as $_content )
{
$options[ $_content['key'] ] = $_content['value'];
}
}
$options['options'] = $options;
break;
}
if ( class_exists( $class ) )
{
$field = new $class( "core_theme_setting_title_{$data['sc_id']}", $value, false, $options );
$field->label = \IPS\Member::loggedIn()->language()->addToStack( $data['sc_title'], FALSE, array( 'escape' => true ) );
$form->add( $field );
}
else
{
\IPS\Log::log( 'VSE tried to load ' . $class . ' for theme setting ' . $key, 'vse_error' );
}
}
}
/* Template */
/* @note The first param used to be the VSE CSS, however as of 4.5 we handle the VSE differently and it is no longer used */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'vse', 'core', 'front' )->globalTemplate( NULL, $customCss, $settingKeyValues, (string) $form, $theme->skin_gen_data, $colorValues );
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output, 200, 'text/html' );
}
/**
* Close the VSE
*
* @return void
*/
public function close()
{
\IPS\Session::i()->csrfCheck();
/* Update the current member */
\IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] = 0;
\IPS\Member::loggedIn()->save();
\IPS\Request::i()->setCookie( 'vseThemeId', 0 );
\IPS\Output::i()->json( 'ok' );
}
/**
* Home
*
* @return void
*/
protected function home()
{
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->skin = (int) \IPS\Request::i()->id;
\IPS\Member::loggedIn()->save();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
}
/**
* Execute
*
* @return void
*/
public function build()
{
if ( ! \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_easy_editor' ) OR ! \IPS\Member::loggedIn()->isAdmin() OR ! \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] )
{
\IPS\Output::i()->json( 'NO_PERMISSION', 403 );
}
\IPS\Session::i()->csrfCheck();
$theme = null;
if ( isset( \IPS\Request::i()->cookie['vseThemeId'] ) )
{
$theme = \IPS\Theme::load( \intval( \IPS\Request::i()->cookie['vseThemeId'] ) );
if ( ! $theme->by_skin_gen )
{
$theme = null;
}
}
if ( $theme === null )
{
\IPS\Output::i()->json( 'NO_PERMISSION', 403 );
}
$theme->vseSave( \IPS\Request::i()->colors, \IPS\Request::i()->customcss, \IPS\Request::i()->settings );
\IPS\File::getClass('core_Theme')->deleteContainer( 'css_built_' . $theme->id );
$theme->css_map = array();
$theme->save();
foreach( $theme->children() as $child )
{
\IPS\File::getClass('core_Theme')->deleteContainer( 'css_built_' . $child->id );
$child->css_map = array();
$child->save();
}
\IPS\Output::i()->json( array( 'status' => 'ok', 'theme_set_id' => $theme->id ) );
}
}
@@ -11,77 +11,92 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\Content\Controller;
use IPS\core\Warnings\Reason;
use IPS\core\Warnings\Warning;
use IPS\DateTime;
use IPS\Db;
use IPS\Helpers\Table\Content;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
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;
}
/**
* Member Warnings
*/
class _warnings extends \IPS\Content\Controller
class warnings extends Controller
{
/**
* [Content\Controller] Class
*/
protected static $contentModel = 'IPS\core\Warnings\Warning';
protected static string $contentModel = 'IPS\core\Warnings\Warning';
/**
* Rebuilt FURL
*/
protected $rebuiltUrl = NULL;
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
parent::execute();
if ( !\IPS\Settings::i()->warn_on )
if ( !Settings::i()->warn_on )
{
\IPS\Output::i()->error( 'warning_system_disabled', '2C184/7', 403, '' );
Output::i()->error( 'warning_system_disabled', '2C184/7', 403, '' );
}
}
/**
* View List
*
* @return void
* @return mixed
*/
protected function manage()
protected function manage() : mixed
{
/* Load the member */
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
if ( !$member->member_id )
{
\IPS\Output::i()->error( 'node_error', '2C135/A', 403, '' );
Output::i()->error( 'node_error', '2C135/A', 403, '' );
}
/* Check permission */
if ( !( \IPS\Settings::i()->warn_on AND ( \IPS\Member::loggedIn()->modPermission('mod_see_warn') or ( \IPS\Settings::i()->warn_show_own and \IPS\Member::loggedIn()->member_id == $member->member_id ) ) ) )
if ( !( Settings::i()->warn_on AND ( Member::loggedIn()->modPermission('mod_see_warn') or ( Settings::i()->warn_show_own and Member::loggedIn()->member_id == $member->member_id ) ) ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
}
$table = new \IPS\Helpers\Table\Content( 'IPS\core\Warnings\Warning', \IPS\Http\Url::internal( "app=core&module=system&controller=warnings&id={$member->member_id}", 'front', 'warn_list', $member->members_seo_name ), array( array( 'wl_member=?', $member->member_id ) ) );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' ), 'warningRow' );
$table = new Content( 'IPS\core\Warnings\Warning', Url::internal( "app=core&module=system&controller=warnings&id={$member->member_id}", 'front', 'warn_list', $member->members_seo_name ), array( array( 'wl_member=?', $member->member_id ) ) );
$table->rowsTemplate = array( Theme::i()->getTemplate( 'system', 'core', 'front' ), 'warningRow' );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('members_warnings', FALSE, array( 'sprintf' => array( $member->name ) ) );
\IPS\Output::i()->breadcrumb[] = array( $member->url(), $member->name );
Output::i()->title = Member::loggedIn()->language()->addToStack('members_warnings', FALSE, array( 'sprintf' => array( $member->name ) ) );
Output::i()->breadcrumb[] = array( $member->url(), $member->name );
if( !\IPS\Request::i()->isAjax() )
if( !Request::i()->isAjax() )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'tables', 'core' )->container( (string) $table );
Output::i()->output = Theme::i()->getTemplate( 'tables', 'core' )->container( (string) $table );
}
else
{
\IPS\Output::i()->output = (string) $table;
Output::i()->output = (string) $table;
}
return null;
}
/**
@@ -89,31 +104,31 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function view()
protected function view() : void
{
/* Load the member */
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
if ( !$member->member_id )
{
\IPS\Output::i()->error( 'node_error', '2C135/4', 403, '' );
Output::i()->error( 'node_error', '2C135/4', 403, '' );
}
/* Load it */
try
{
$warning = \IPS\core\Warnings\Warning::loadAndCheckPerms( \IPS\Request::i()->w );
$warning = Warning::loadAndCheckPerms( Request::i()->w );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C184/3', 404, '' );
Output::i()->error( 'node_error', '2C184/3', 404, '' );
}
/* Show it */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'view_warning_details' );
\IPS\Output::i()->breadcrumb[] = array( $member->url(), $member->name );
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=system&controller=warnings&id={$member->member_id}", NULL, 'warn_list', $member->members_seo_name ), \IPS\Member::loggedIn()->language()->addToStack('members_warnings', FALSE, array( 'sprintf' => array( $member->name ) ) ) );
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'view_warning_details' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('modcp')->warnHovercard( $warning );
Output::i()->title = Member::loggedIn()->language()->addToStack( 'view_warning_details' );
Output::i()->breadcrumb[] = array( $member->url(), $member->name );
Output::i()->breadcrumb[] = array( Url::internal( "app=core&module=system&controller=warnings&id={$member->member_id}", NULL, 'warn_list', $member->members_seo_name ), Member::loggedIn()->language()->addToStack('members_warnings', FALSE, array( 'sprintf' => array( $member->name ) ) ) );
Output::i()->breadcrumb[] = array( NULL, Member::loggedIn()->language()->addToStack( 'view_warning_details' ) );
Output::i()->output = Theme::i()->getTemplate('modcp')->warnHovercard( $warning );
}
/**
@@ -121,32 +136,32 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function warn()
protected function warn() : void
{
/* Load the member */
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
if ( !$member->member_id )
{
\IPS\Output::i()->error( 'node_error', '2C135/2', 403, '' );
Output::i()->error( 'node_error', '2C135/2', 403, '' );
}
/* Permission Check */
if ( !\IPS\Member::loggedIn()->canWarn( $member ) )
if ( !Member::loggedIn()->canWarn( $member ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C184/6', 403, '' );
Output::i()->error( 'no_module_permission', '2C184/6', 403, '' );
}
/* Build the form */
$form = \IPS\core\Warnings\Warning::create();
$form->class = 'ipsForm_vertical';
$form = Warning::create();
$form->class = 'ipsForm--vertical ipsForm--warn';
$form->attributes = array( 'data-controller' => 'core.front.modcp.warnForm', 'data-member' => $member->member_id );
$form->hiddenValues['ref'] = \IPS\Request::i()->ref;
$form->hiddenValues['ref'] = Request::i()->ref;
$form->hiddenValues['member'] = $member->member_id;
/* Display */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_modcp.js', 'core' ) );
$actions = \IPS\Db::i()->select( '*', 'core_members_warn_actions', NULL, 'wa_points ASC' );
if ( \count( $actions ) )
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front_modcp.js', 'core' ) );
$actions = Db::i()->select( '*', 'core_members_warn_actions', NULL, 'wa_points ASC' );
if ( count( $actions ) )
{
$min = NULL;
foreach ( $actions as $a )
@@ -158,10 +173,10 @@ class _warnings extends \IPS\Content\Controller
break;
}
$form->addSidebar( \IPS\Theme::i()->getTemplate( 'modcp' )->warnActions( $actions, $member, $min ) );
$form->addSidebar( Theme::i()->getTemplate( 'modcp' )->warnActions( $actions, $member, $min ) );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('warn_member', FALSE, array( 'sprintf' => array( $member->name ) ) );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
Output::i()->title = Member::loggedIn()->language()->addToStack('warn_member', FALSE, array( 'sprintf' => array( $member->name ) ) );
Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
/**
@@ -169,47 +184,46 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function acknowledge()
protected function acknowledge() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
/* Load the member */
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
if ( !$member->member_id )
{
\IPS\Output::i()->error( 'node_error', '2C184/4', 403, '' );
Output::i()->error( 'node_error', '2C184/4', 403, '' );
}
/* Load it */
try
{
$warning = \IPS\core\Warnings\Warning::loadAndCheckPerms( \IPS\Request::i()->w );
$warning = Warning::loadAndCheckPerms( Request::i()->w );
if ( $warning->member !== $member->member_id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C184/5', 404, '' );
Output::i()->error( 'node_error', '2C184/5', 404, '' );
}
/* Acknowledge it */
$warning->acknowledged = TRUE;
$warning->save();
/* @note SELECT_FROM_WRITE_SERVER: Avoid issue where warning may be in writer table, but not in reader */
$member->members_bitoptions['unacknowledged_warnings'] = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_members_warn_logs', array( "wl_member=? AND wl_acknowledged=0", $member->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$member->members_bitoptions['unacknowledged_warnings'] = (bool) Db::i()->select( 'COUNT(*)', 'core_members_warn_logs', array( "wl_member=? AND wl_acknowledged=0", $member->member_id ), NULL, NULL, NULL, NULL, Db::SELECT_FROM_WRITE_SERVER )->first();
$member->save();
/* Redirect */
if ( $redirectTo = \IPS\Request::i()->referrer() )
if ( $redirectTo = Request::i()->referrer() )
{
\IPS\Output::i()->redirect( $redirectTo );
Output::i()->redirect( $redirectTo );
}
else
{
\IPS\Output::i()->redirect( $warning->url() );
Output::i()->redirect( $warning->url() );
}
}
@@ -218,39 +232,40 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function delete()
protected function delete() : void
{
$class = static::$contentModel;
try
{
$item = $class::loadAndCheckPerms( \IPS\Request::i()->w );
$member = \IPS\Member::load( $item->member );
/* @var Warning $class */
$item = $class::loadAndCheckPerms( Request::i()->w );
$member = Member::load( $item->member );
if ( $item->canDelete() )
{
if ( isset( \IPS\Request::i()->undo ) )
if ( isset( Request::i()->undo ) )
{
\IPS\Session::i()->csrfCheck();
if ( \IPS\Request::i()->undo )
Session::i()->csrfCheck();
if ( Request::i()->undo )
{
$item->undo();
}
$item->delete();
\IPS\Output::i()->redirect( $member->url(), 'warn_revoked' );
Output::i()->redirect( $member->url(), 'warn_revoked' );
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('modcp')->warningRevoke( $item );
Output::i()->output = Theme::i()->getTemplate('modcp')->warningRevoke( $item );
}
}
else
{
\IPS\Output::i()->error( 'generic_error', '2C184/1', 403, '' );
Output::i()->error( 'generic_error', '2C184/1', 403, '' );
}
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C184/2', 404, '' );
Output::i()->error( 'node_error', '2C184/2', 404, '' );
}
}
@@ -259,12 +274,12 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function reasonAjax()
protected function reasonAjax() : void
{
/* Check permission */
if ( ! ( \IPS\Settings::i()->warn_on AND \IPS\Member::loggedIn()->modPermission('mod_see_warn') ) )
if ( ! ( Settings::i()->warn_on AND Member::loggedIn()->modPermission('mod_see_warn') ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
}
$remove = array(
@@ -273,9 +288,9 @@ class _warnings extends \IPS\Content\Controller
'unlimited' => TRUE,
);
if ( \IPS\Request::i()->id == 'other' )
if ( Request::i()->id == 'other' )
{
\IPS\Output::i()->json( array(
Output::i()->json( array(
'points' => 0,
'points_override' => TRUE,
'remove' => $remove,
@@ -288,19 +303,19 @@ class _warnings extends \IPS\Content\Controller
try
{
$reason = \IPS\core\Warnings\Reason::load( \IPS\Request::i()->id );
$reason = Reason::load( Request::i()->id );
/* Add in the remove properties */
if ( $reason->remove AND $reason->remove != -1 )
{
$date = \IPS\DateTime::create();
$date = DateTime::create();
if ( $reason->remove_unit == 'h' )
{
$date->add( new \DateInterval( "PT{$reason->remove}H" ) );
$date->add( new DateInterval( "PT{$reason->remove}H" ) );
}
else
{
$date->add( new \DateInterval( "P{$reason->remove}D" ) );
$date->add( new DateInterval( "P{$reason->remove}D" ) );
}
$remove = array(
@@ -310,7 +325,7 @@ class _warnings extends \IPS\Content\Controller
);
}
\IPS\Output::i()->json( array(
Output::i()->json( array(
'points' => $reason->points,
'points_override' => $reason->points_override,
'remove' => $remove,
@@ -321,9 +336,9 @@ class _warnings extends \IPS\Content\Controller
) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
\IPS\Output::i()->json( array(
Output::i()->json( array(
'points' => 0,
'points_override' => FALSE,
'remove' => $remove,
@@ -340,7 +355,7 @@ class _warnings extends \IPS\Content\Controller
*
* @return void
*/
protected function actionAjax()
protected function actionAjax() : void
{
$actions = array(
'mq' => array(
@@ -360,17 +375,17 @@ class _warnings extends \IPS\Content\Controller
),
);
$member = \IPS\Member::load( \IPS\Request::i()->member );
$member = Member::load( Request::i()->member );
/* Check permission */
if ( !( \IPS\Settings::i()->warn_on AND ( \IPS\Member::loggedIn()->modPermission('mod_see_warn') or ( \IPS\Settings::i()->warn_show_own and \IPS\Member::loggedIn()->member_id == $member->member_id ) ) ) )
if ( !( Settings::i()->warn_on AND ( Member::loggedIn()->modPermission('mod_see_warn') or ( Settings::i()->warn_show_own and Member::loggedIn()->member_id == $member->member_id ) ) ) )
{
\IPS\Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
Output::i()->error( 'no_module_permission', '2C135/9', 403, '' );
}
try
{
$action = \IPS\Db::i()->select( '*', 'core_members_warn_actions', array( 'wa_points<=?', ( $member->warn_level + (int) \IPS\Request::i()->points ) ), 'wa_points DESC', 1 )->first();
$action = Db::i()->select( '*', 'core_members_warn_actions', array( 'wa_points<=?', ( $member->warn_level + (int) Request::i()->points ) ), 'wa_points DESC', 1 )->first();
foreach ( array( 'mq', 'rpa', 'suspend' ) as $k )
{
if ( $action[ 'wa_' . $k ] == -1 )
@@ -379,18 +394,18 @@ class _warnings extends \IPS\Content\Controller
}
elseif ( $action[ 'wa_' . $k ] )
{
$date = \IPS\DateTime::ts( time() )->add( new \DateInterval( $action[ 'wa_' . $k . '_unit' ] == 'h' ? "PT{$action[ 'wa_' . $k ]}H" : "P{$action[ 'wa_' . $k ]}D" ) );
$date = DateTime::ts( time() )->add( new DateInterval( $action[ 'wa_' . $k . '_unit' ] == 'h' ? "PT{$action[ 'wa_' . $k ]}H" : "P{$action[ 'wa_' . $k ]}D" ) );
$actions[ $k ]['date'] = $date->format( 'Y-m-d' );
$actions[ $k ]['time'] = $date->format( 'H:i' );
}
}
}
catch ( \UnderflowException $e ) { }
catch ( UnderflowException $e ) { }
\IPS\Output::i()->json( array(
Output::i()->json( array(
'actions' => $actions,
'override' => isset( $action ) ? $action['wa_override'] : \IPS\Member::loggedIn()->modPermission('warning_custom_noaction')
'override' => isset( $action ) ? $action['wa_override'] : Member::loggedIn()->modPermission('warning_custom_noaction')
) );
}
}
+451 -194
View File
@@ -11,28 +11,74 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use Exception;
use IPS\Application;
use IPS\cms\Blocks\Block;
use IPS\cms\widgets\Blocks;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form;
use IPS\Member;
use IPS\Output;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use IPS\Widget;
use IPS\Widget\Area;
use IPS\Settings;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function explode;
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;
}
/**
* Sidebar Widgets
*/
class _widgets extends \IPS\Dispatcher\Controller
class widgets extends Controller
{
/**
* The widget class to use for configuration
*
* @var string
*/
protected string $widgetClass = Widget::class;
protected string $pageApp = '';
protected string $pageModule = '';
protected string $pageController = '';
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
/* Check Permissions */
if ( ! \IPS\Member::loggedIn()->modPermission('can_manage_sidebar') )
if ( ! Member::loggedIn()->modPermission('can_manage_sidebar') )
{
\IPS\Output::i()->error( 'no_permission_manage_sidebar', '2S172/1', 403, '' );
Output::i()->error( 'no_permission_manage_sidebar', '2S172/1', 403, '' );
}
$this->pageApp = Request::i()->pageApp ?? '';
$this->pageModule = Request::i()->pageModule ?? '';
$this->pageController = Request::i()->pageController ?? '';
if( isset( Request::i()->pageArea ) AND Request::i()->pageArea == Area::AREA_GLOBAL_FOOTER )
{
/* Re-assign values to global */
$this->pageApp = 'global';
$this->pageModule = 'global';
$this->pageController = 'global';
}
parent::execute();
@@ -43,56 +89,64 @@ class _widgets extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function getBlockList()
protected function getBlockList() : void
{
$availableBlocks = array();
foreach ( \IPS\Db::i()->select( "*", 'core_widgets' ) as $widget )
/* Initialize with Pages as the first in the list */
$availableBlocks = array(
'cms' => []
);
$favoriteIds = json_decode( Settings::i()->favorite_blocks, true );
$favorites = [];
$customBlocks = [];
/* Loop through applications and get all available blocks */
foreach( Application::enabledApplications() as $application )
{
try
{
$appOrPlugin = isset( $widget['plugin'] ) ? \IPS\Plugin::load( $widget['plugin'] ) : \IPS\Application::load( $widget['app'] );
if ( isset( $widget['plugin'] ) )
{
if ( !$appOrPlugin->enabled )
{
continue;
}
}
else
{
if ( !\IPS\Application::appIsEnabled( $appOrPlugin->directory ) or !\IPS\Application::load( $appOrPlugin->directory )->canAccess() )
{
continue;
}
}
$block = \IPS\Widget::load( $appOrPlugin, $widget['key'], mt_rand(), array(), $widget['restrict'], null );
$block->allowReuse = (boolean) $widget['allow_reuse'];
$block->menuStyle = $widget['menu_style'];
if ( ! $block->isExecutableByApp( array( \IPS\Request::i()->pageApp, 'sidebar' ) ) )
{
throw new \OutOfRangeException;
}
}
catch( \Exception $e )
if( !$application->canAccess() )
{
continue;
}
if( isset( $widget['app'] ) )
foreach( $application->getAvailableWidgets() as $block )
{
$availableBlocks['apps'][ $widget['app'] ][] = $block;
}
else
{
$availableBlocks['plugin'][ $widget['plugin'] ][] = $block;
if ( !$block->isExecutableByApp( array( $this->pageApp, Area::AREA_SIDEBAR ) ) )
{
continue;
}
if ( in_array( $block->key, $favoriteIds ) )
{
$favorites[$block->key] = $block;
}
if ( $block instanceof Blocks )
{
foreach ( new ActiveRecordIterator( Db::i()->select( "*", "cms_blocks" ), Block::class ) as $customBlock )
{
$title = $customBlock->_title;
Member::loggedIn()->language()->parseOutputForDisplay( $title );
$customBlocks[$title] = [$block, $customBlock];
if ( in_array( $block->key . ":" . $title, $favoriteIds ) )
{
$favorites[$block->key . ":" . $title] = [$block, $customBlock];
}
}
}
$availableBlocks[ $application->directory ][] = $block;
}
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'widgets' )->blockList( $availableBlocks );
$favoritesSorted = [];
foreach ( $favoriteIds as $id )
{
if ( isset( $favorites[$id] ) )
{
$favoritesSorted[$id] = $favorites[$id];
}
}
Output::i()->output = Theme::i()->getTemplate( 'widgets' )->blockList( $availableBlocks, $favoritesSorted, $customBlocks );
}
/**
@@ -100,54 +154,73 @@ class _widgets extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function getBlock()
protected function getBlock() : void
{
$key = $block = explode( "_", \IPS\Request::i()->blockID );
$key = explode( "_", Request::i()->blockID );
$area = null;
$areaBlocks = null;
if ( isset( \IPS\Request::i()->pageApp ) )
/* @var Widget $widgetClass */
$widgetClass = $this->widgetClass;
foreach( $this->getAreasFromDatabase() as $item )
{
try
$blocks = $item->getAllWidgets();
if( Request::i()->pageArea == $item->id )
{
foreach ( json_decode( \IPS\Db::i()->select( 'widgets', 'core_widget_areas', array( 'app=? AND module=? AND controller=? AND area=?', \IPS\Request::i()->pageApp, \IPS\Request::i()->pageModule, \IPS\Request::i()->pageController, \IPS\Request::i()->pageArea ) )->first(), TRUE ) as $k => $block )
{
switch( $key[0] )
{
case 'app':
$area = $item;
$areaBlocks = $blocks;
}
if( ( isset( $block['app'] ) and $block['app'] == $key[1] ) AND $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$widget = \IPS\Widget::load( \IPS\Application::load( $block['app'] ), $block['key'], $block['unique'], $block['configuration'], null, \IPS\Request::i()->orientation );
break 2;
}
break;
case 'plugin':
if( ( isset( $block['plugin'] ) and $block['plugin'] == $key[1] ) AND $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$widget = \IPS\Widget::load( \IPS\Plugin::load( $block['plugin'] ), $block['key'], $block['unique'], $block['configuration'], null, \IPS\Request::i()->orientation );
break 2;
}
break;
}
foreach( $blocks as $block )
{
if( isset( $block['key'] ) AND $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$config = ( isset( $block['configuration'] ) AND !empty( $block['configuration'] ) ) ? $block['configuration'] : $widgetClass::getConfiguration( $key[3] );
$widget = Widget::load( Application::load( $block['app'] ), $block['key'], $block['unique'], $config, null, Request::i()->orientation, Request::i()->layout ?: '' );
break;
}
}
catch ( \OutOfRangeException $e ) { }
catch ( \UnderflowException $e ) { }
}
if ( !isset( $widget ) )
{
try
{
$widget = \IPS\Widget::load( ( $key[0] == 'plugin' ) ? \IPS\Plugin::load( $key[1] ) : \IPS\Application::load( $key[1] ), $key[2], $key[3], array(), null, \IPS\Request::i()->orientation );
}
catch( \OutOfRangeException $e )
{
$widget = '';
}
}
\IPS\Output::i()->json( array( 'html' => (string) $widget, 'devices' => ( isset( $widget->configuration['devices_to_show'] ) ) ? $widget->configuration['devices_to_show'] : array('Phone', 'Tablet', 'Desktop') ) );
if ( !isset( $widget ) )
{
$config = json_decode( Request::i()->defaultConfiguration ?: '""', true );
$config = is_array( $config ) ? $config : ( ( isset( $key[3] ) and $key[3] ) ? $widgetClass::getConfiguration( $key[3] ) : array() );
$widget = Widget::load( Application::load( $key[1] ), $key[2], ( $key[3] ?? '' ), $config, null, Request::i()->orientation );
// it's possible we want to save it
try
{
if ( Request::i()->createIfDoesNotExist and Request::i()->pageArea and is_array( $areaBlocks ) )
{
Session::i()->csrfCheck();
$newBlock = [
'app' => $key[1],
'key' => $key[2],
'unique' => $key[3],
'configuration' => $config
];
$areaBlocks[] = $newBlock;
if( $area === null )
{
$area = Area::create( Request::i()->pageArea, $areaBlocks );
}
else
{
$area->addWidget( $newBlock );
}
$this->saveArea( $area );
Widget::deleteCaches();
}
}
catch ( OutOfRangeException ) {}
}
$output = (string) $widget;
Output::i()->output = ( $output ) ?: Theme::i()->getTemplate( 'widgets', 'core', 'front' )->blankWidget( $widget );
}
/**
@@ -155,67 +228,63 @@ class _widgets extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function getConfiguration()
protected function getConfiguration() : void
{
$key = explode( "_", \IPS\Request::i()->block );
$blocks = array();
$key = explode( "_", Request::i()->block );
/* @var Widget $widgetClass */
$widgetClass = $this->widgetClass;
try
{
$where = ( $key[0] ) == 'app' ? '`key`=? AND `app`=?' : '`key`=? AND `plugin`=?';
$widgetMaster = \IPS\Db::i()->select( '*', 'core_widgets', array( $where, $key[2], $key[1] ) )->first();
$blocks = \IPS\Db::i()->select( '*', 'core_widget_areas', array( 'app=? AND module=? AND controller=? AND area=?', \IPS\Request::i()->pageApp, \IPS\Request::i()->pageModule, \IPS\Request::i()->pageController, \IPS\Request::i()->pageArea ) )->first();
$blocks = json_decode( $blocks['widgets'], TRUE );
$widgetMaster = Db::i()->select( '*', 'core_widgets', array( '`key`=? AND `app`=?', $key[2], $key[1] ) )->first();
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e ){}
$blocks = [];
/* @var Area $currentArea */
$currentArea = $this->getAreasFromDatabase()[ Request::i()->pageArea ] ?? null;
if( $currentArea !== null )
{
switch( $key[0] )
{
case 'app':
$blocks = array( array( 'app' => $key[1], 'key' => $key[2], 'unique' => $key[3], 'configuration' => array() ) );
break;
case 'plugin':
$blocks = array( array( 'plugin' => $key[1], 'key' => $key[2], 'unique' => $key[3], 'configuration' => array() ) );
break;
}
$blocks = $currentArea->getAllWidgets();
}
$widget = NULL;
$widget = NULL;
$requestedBlock = $key[3] ?? '';
if( !empty( $blocks ) )
{
foreach ( $blocks as $k => $block )
{
switch( $key[0] )
if ( $requestedBlock and (string) $k !== $requestedBlock )
{
case 'app':
if( ( isset( $block['app'] ) AND $block['app'] == $key[1] ) AND $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$widget = \IPS\Widget::load( \IPS\Application::load( $block['app'] ), $block['key'], $block['unique'], $block['configuration'] );
}
break;
case 'plugin':
if( ( isset( $block['plugin'] ) AND $block['plugin'] == $key[1] ) AND $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$widget = \IPS\Widget::load( \IPS\Plugin::load( $block['plugin'] ), $block['key'], $block['unique'], $block['configuration'] );
}
break;
continue;
}
if ( $block['key'] == $key[2] AND $block['unique'] == $key[3] )
{
$config = ( isset( $block['configuration'] ) AND !empty( $block['configuration'] ) ) ? $block['configuration'] : $widgetClass::getConfiguration( $key[3] );
$widget = Widget::load( Application::load( $block['app'] ), $block['key'], $block['unique'], $config ?? [] );
$widget->menuStyle = $widgetMaster['menu_style'] ?? 'menu';
if ( isset( $widgetMaster ) and $widgetMaster['layouts'] === '*' and $widget->isCustomizableWidget() )
{
$widget->layouts = Area::$allowedWrapBehaviors;
}
else if ( !empty( $widgetMaster['layouts'] ) )
{
$widget->layouts = array_intersect( Area::$allowedWrapBehaviors, explode( ',', $widgetMaster['layouts'] ) );
}
else
{
$widget->layouts = isset( $widgetMaster['default_layout'] ) ? [ $widgetMaster['default_layout'] ] : ['wrap'];
}
}
if( $widget !== NULL AND method_exists( $widget, 'configuration' ) )
{
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->jsFiles = array();
}
$widget->menuStyle = $widgetMaster['menu_style'];
$form = new \IPS\Helpers\Form( 'form', 'saveSettings' );
if ( $configurationForm = $widget->configuration( $form ) )
$form = new Form( 'form', 'saveSettings' );
if ( $widget->configuration( $form ) !== NULL )
{
if ( $values = $form->values() )
{
@@ -223,31 +292,23 @@ class _widgets extends \IPS\Dispatcher\Controller
{
$values = $widget->preConfig( $values );
}
/* Special advanced builder stuff */
if ( \in_array( 'IPS\Widget\Builder', class_implements( $widget ) ) )
if( $widget->isBuilderWidget() )
{
if( isset( $values['widget_adv__background_custom_image'] ) and $values['widget_adv__background_custom_image'] )
{
$values['widget_adv__background_custom_image'] = (string) $values['widget_adv__background_custom_image'];
}
}
if( isset( $values['show_on_all_devices'] ) and $values['show_on_all_devices'] )
{
$values['devices_to_show'] = array( 'Phone', 'Tablet', 'Desktop' );
}
unset( $values['show_on_all_devices'] );
$blocks[ $k ]['configuration'] = $values;
\IPS\Db::i()->insert( 'core_widget_areas', array( 'app' => \IPS\Request::i()->pageApp, 'module' => \IPS\Request::i()->pageModule, 'controller' => \IPS\Request::i()->pageController, 'area' => \IPS\Request::i()->pageArea, 'widgets' => json_encode( $blocks ) ), TRUE );
\IPS\Widget::deleteCaches( $block['key'], ( isset( $block['app'] ) ) ? $block['app'] : NULL, ( isset( $block['plugin'] ) ) ? $block['plugin'] : NULL );
\IPS\Output::i()->json( 'OK' );
$actualArea = $currentArea->addWidget( $blocks[ $k ] );
$this->saveArea( $currentArea );
Output::i()->json( [ 'blocks' => $blocks[$k], 'areaClasses' => $actualArea->classes() ] );
}
\IPS\Member::loggedIn()->language()->words['widget_adv__custom_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'widget_adv__custom__desc', FALSE, array( 'sprintf' => array( \IPS\Request::i()->block ) ) );
\IPS\Output::i()->output = $configurationForm->customTemplate( array( \IPS\Theme::i()->getTemplate( 'widgets', 'core' ), 'formTemplate' ), $widget );
Output::i()->output = $widget->configuration()->customTemplate( array( Theme::i()->getTemplate( 'widgets', 'core' ), 'formTemplate' ), $widget );
}
}
}
@@ -259,43 +320,30 @@ class _widgets extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function saveOrder()
protected function saveOrder() : void
{
if( !\in_array( \IPS\Request::i()->area, array( 'sidebar', 'header', 'footer' ) ) )
{
\IPS\Output::i()->error( 'invalid_widget_area', '3S172/2', 403, '' );
}
\IPS\Session::i()->csrfCheck();
$newOrder = array();
$seen = array();
try
$widgets = array();
Session::i()->csrfCheck();
$currentConfig = $this->getAreasFromDatabase()[ Request::i()->area ] ?? null;
if( $currentConfig )
{
$currentConfig = \IPS\Db::i()->select( '*', 'core_widget_areas', array( 'app=? AND module=? AND controller=? AND area=?', \IPS\Request::i()->pageApp, \IPS\Request::i()->pageModule, \IPS\Request::i()->pageController, \IPS\Request::i()->area ) )->first();
$widgets = json_decode( $currentConfig['widgets'], TRUE );
if( !is_countable( $widgets ) )
{
throw new \UnderflowException;
}
$widgets = $currentConfig->getAllWidgets();
}
catch ( \UnderflowException $e )
{
$widgets = array();
}
/* Loop over the new order and merge in current blocks so we don't lose config */
if ( isset ( \IPS\Request::i()->order ) )
if ( isset ( Request::i()->order ) )
{
foreach ( \IPS\Request::i()->order as $block )
foreach ( Request::i()->order as $block )
{
$block = explode( "_", $block );
$added = FALSE;
foreach( $widgets as $widget )
{
if ( $widget['key'] == $block[2] AND $widget['unique'] == $block[3] )
if ( $widget['key'] == $block[2] and $widget['unique'] == $block[3] )
{
$seen[] = $widget['unique'];
$newOrder[] = $widget;
@@ -303,26 +351,18 @@ class _widgets extends \IPS\Dispatcher\Controller
break;
}
}
if( !$added )
{
$newBlock = array();
if ( $block[0] == 'app' )
{
$newBlock['app'] = $block[1];
}
else
{
$newBlock['plugin'] = $block[1];
}
$newBlock['key'] = $block[2];
$newBlock['unique'] = $block[3];
$newBlock['configuration'] = array();
/* Make sure this widget doesn't have configuration in another area */
$newBlock['configuration'] = \IPS\Widget::getConfiguration( $newBlock['unique'] );
/* @var Widget $widgetClass */
$widgetClass = $this->widgetClass;
$newBlock = [
'app' => $block[1],
'key' => $block[2],
'unique' => $block[3],
'configuration' => $widgetClass::getConfiguration( $block[3] )
];
$seen[] = $block[3];
$newOrder[] = $newBlock;
}
@@ -330,23 +370,240 @@ class _widgets extends \IPS\Dispatcher\Controller
}
/* Anything to update? */
if ( \count( $widgets ) > \count( $newOrder ) )
if ( count( $widgets ) > count( $newOrder ) )
{
/* No items left in area, or one has been removed */
foreach( $widgets as $widget )
{
/* If we haven't seen this widget, it's been removed, so add to trash */
if ( ! \in_array( $widget['unique'], $seen ) )
if ( ! in_array( $widget['unique'], $seen ) )
{
\IPS\Widget::trash( $widget['unique'], $widget );
Widget::trash( $widget['unique'], $widget );
}
}
}
/* Expire Caches so up to date information displays */
\IPS\Widget::deleteCaches();
/* Check core_widget_areas to ensure that the block wasn't added there */
if ( isset( Request::i()->exclude ) and ! empty( Request::i()->exclude ) )
{
$bits = explode( "_", Request::i()->exclude );
$this->_checkAndDeleteFromCoreWidgets( $bits[3], $seen );
}
/* Save to database */
\IPS\Db::i()->replace( 'core_widget_areas', array( 'app' => \IPS\Request::i()->pageApp, 'module' => \IPS\Request::i()->pageModule, 'controller' => \IPS\Request::i()->pageController, 'widgets' => json_encode( $newOrder ), 'area' => \IPS\Request::i()->area ) );
/* Expire Caches so up to date information displays */
Widget::deleteCaches();
/* Overwrite the entire area */
$newArea = Area::create( Request::i()->area, $newOrder );
$this->saveArea( $newArea );
}
/**
* Remove the block from the page area
*
* @return void
*/
protected function removeBlock() : void
{
Session::i()->csrfCheck();
try
{
$uniques = [];
foreach ( Request::i()->blockIDs as $blockID )
{
$key = explode( '_', $blockID );
$uniques[] = $key[3];
}
/* For deletion, only remove the blocks in the area. When moving a block from one area to another, the remove block call is separate */
foreach ( $this->getAreasFromDatabase( Request::i()->area ?: null ) as $widgetArea )
{
foreach( $uniques as $unique )
{
$widgetArea->removeWidget( $unique );
Db::i()->delete( 'core_widgets_config', [ 'id=?', $unique ] );
}
$this->saveArea( $widgetArea );
}
}
catch ( OutOfRangeException ) {}
Output::i()->json( [ 'message' => 'deleted' ], 201 );
}
/**
* @return void
*/
protected function saveWidgetTree() : void
{
/* First, load all the widgets in the page. It is possible they moved a widget from one area to another in the same page, so we need to copy the configuration from those areas */
$existingWidgets = [];
foreach ( $this->getAreasFromDatabase() as $row )
{
$existingWidgets = array_merge( $existingWidgets, $row->getAllWidgets() );
}
/* Clean the tree, sometimes there are duplicates */
$tree = Area::cleanTreeData( json_decode( Request::i()->tree, true ) );
/* Create a new area */
$area = new Area( $tree, Request::i()->pageArea );
$currentWidgets = $area->getAllWidgets();
/* The tree data does not contain block configuration, so we put that back in */
foreach ( $existingWidgets as $existing )
{
if ( $area->replaceWidget( $existing ) )
{
$currentWidgets[$existing['unique']] = $existing;
}
}
$this->saveArea( $area );
$output = [
'message' => 'saved',
'area_tree' => $area->toArray(),
];
if ( defined( 'PAGEBUILDER_DEV' ) and \PAGEBUILDER_DEV )
{
$output['area_tree'] = $area->toArray();
$output['currentWidgets'] = $currentWidgets;
$output['existingWidgets'] = $existingWidgets;
}
Output::i()->json( $output, 201 );
}
/**
* Get an array containing all the areas in this page
*
* @param string|null $area The area to filter by; by default (null) it will get all areas *
* @return Area[] Returns an array mapping the widget areas to the widgets in that area
*/
public function getAreasFromDatabase( ?string $area=null ) : array
{
return Area::getAreasFromDatabase( $this->pageApp, $this->pageModule, $this->pageController, $area );
}
/**
* Save an area to the database and link it to the page
*
* @param Area $area
* @return void
*/
public function saveArea( Area $area ) : void
{
$pageApp = Request::i()->pageApp;
$pageModule = Request::i()->pageModule;
$pageController = Request::i()->pageController;
if ( $area->id == 'globalfooter' )
{
/* Re-assign values to global */
$pageApp = 'global';
$pageModule = 'global';
$pageController = 'global';
}
/* If we have no content, clear this out entirely */
if( !$area->hasWidgets() )
{
Db::i()->delete( 'core_widget_areas', ['area=? AND app=? and module=? and controller=?', $area->id, $pageApp, $pageModule, $pageController ] );
return;
}
/* Store the widget configuration */
foreach( $area->getAllWidgets() as $widget )
{
if( isset( $widget['configuration'] ) AND !empty( $widget['configuration'] ) )
{
Db::i()->replace( 'core_widgets_config', [
'id' => $widget['unique'],
'data' => json_encode( $widget['configuration'] )
] );
}
}
/* Does the area exist? */
try
{
$row = Db::i()->select( '*', 'core_widget_areas', [ 'area=? and app=? and module=? and controller=?', $area->id, $pageApp, $pageModule, $pageController ] )->first();
Db::i()->update( 'core_widget_areas', [
'tree' => json_encode( $area->toArray( true, false ) )
], ['area=? AND app=? and module=? and controller=?', $area->id, $pageApp, $pageModule, $pageController ] );
}
catch( UnderflowException )
{
Db::i()->insert( 'core_widget_areas', [
'app' => $pageApp,
'module' => $pageModule,
'controller' => $pageController,
'area' => $area->id,
'widgets' => '[]',
'tree' => json_encode( $area->toArray( true, false ) )
]);
}
/* Clear caches */
Widget::deleteCaches( null, $pageApp == 'global' ?: null );
}
/**
* Sometimes the widgets end up in the core table. We haven't really found out why this happens. It happens very rarely.
* It may be that the CMS JS mixin doesn't load so the core ajax URLs are used (system/widgets.php) and not the cms widget (page/builder.php).
* This method ensures that any widgets in the core table are removed
*
* @param string $uniqueId The unique key of the widget (eg: wzsj1233)
* @param array $widgets Current widgets (eg from core_widget_areas.widgets (json decoded))
* @return bool True if something removed, false if not
*/
protected function _checkAndDeleteFromCoreWidgets( string $uniqueId, array $widgets ): bool
{
/* Placeholder function so that we can override this in the CMS builder */
return false;
}
/**
* @return void
*/
protected function addFavorite()
{
if ( !isset( Request::i()->blockID ) )
{
Output::i()->json( [ "message" => "expected block id" ], 400 );
}
$favorites = json_decode( Settings::i()->favorite_blocks, true );
if ( !in_array( Request::i()->blockID, $favorites ) )
{
$favorites[] = Request::i()->blockID;
}
Settings::i()->changeValues( [ "favorite_blocks" => json_encode( $favorites ) ] );
Output::i()->json( [ "favorites" => $favorites ], 201 );
}
/**
* @return void
*/
protected function removeFavorite()
{
if ( !isset( Request::i()->blockID ) )
{
Output::i()->json( [ "message" => "expected block id" ], 400 );
}
$favorites = json_decode( Settings::i()->favorite_blocks, true );
$favorites = array_filter( $favorites, function ($val) {
return $val !== Request::i()->blockID;
});
Settings::i()->changeValues( [ "favorite_blocks" => json_encode( $favorites ) ] );
Output::i()->json( [ "favorites" => $favorites ], 201 );
}
}