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

+25 -16
View File
@@ -10,39 +10,48 @@
*/
namespace IPS\core\api\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\Types;
use IPS\core\api\GraphQL\Mutations\ChangeNotificationSetting;
use IPS\core\api\GraphQL\Mutations\DeleteAttachment;
use IPS\core\api\GraphQL\Mutations\Follow;
use IPS\core\api\GraphQL\Mutations\IgnoreUser;
use IPS\core\api\GraphQL\Mutations\MarkNotificationRead;
use IPS\core\api\GraphQL\Mutations\Messenger\AddConversationUser;
use IPS\core\api\GraphQL\Mutations\Messenger\LeaveConversation;
use IPS\core\api\GraphQL\Mutations\Messenger\RemoveConversationUser;
use IPS\core\api\GraphQL\Mutations\Unfollow;
use IPS\core\api\GraphQL\Mutations\UploadAttachment;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Core mutationss GraphQL API
*/
abstract class _Mutation
abstract class Mutation
{
/**
* Get the supported query types in this app
*
* @return array
*/
public static function mutations()
public static function mutations() : array
{
return [
'follow' => new \IPS\core\api\GraphQL\Mutations\Follow(),
'unfollow' => new \IPS\core\api\GraphQL\Mutations\Unfollow(),
'markNotificationRead' => new \IPS\core\api\GraphQL\Mutations\MarkNotificationRead(),
'uploadAttachment' => new \IPS\core\api\GraphQL\Mutations\UploadAttachment(),
'deleteAttachment' => new \IPS\core\api\GraphQL\Mutations\DeleteAttachment(),
'ignoreMember' => new \IPS\core\api\GraphQL\Mutations\IgnoreUser(),
'changeNotificationSetting' => new \IPS\core\api\GraphQL\Mutations\ChangeNotificationSetting(),
'leaveConversation' => new \IPS\core\api\GraphQL\Mutations\Messenger\LeaveConversation(),
'removeConversationUser' => new \IPS\core\api\GraphQL\Mutations\Messenger\RemoveConversationUser(),
'addConversationUser' => new \IPS\core\api\GraphQL\Mutations\Messenger\AddConversationUser(),
'follow' => new Follow(),
'unfollow' => new Unfollow(),
'markNotificationRead' => new MarkNotificationRead(),
'uploadAttachment' => new UploadAttachment(),
'deleteAttachment' => new DeleteAttachment(),
'ignoreMember' => new IgnoreUser(),
'changeNotificationSetting' => new ChangeNotificationSetting(),
'leaveConversation' => new LeaveConversation(),
'removeConversationUser' => new RemoveConversationUser(),
'addConversationUser' => new AddConversationUser(),
];
}
}
@@ -10,25 +10,33 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\core\api\GraphQL\Types\NotificationTypeType;
use IPS\Db;
use IPS\Member;
use IPS\Notification;
use function count;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Change notification setting mutation for GraphQL API
*/
class _ChangeNotificationSetting
class ChangeNotificationSetting
{
/*
* @brief Query description
*/
public static $description = "Change a notification setting";
public static string $description = "Change a notification setting";
/*
* Mutation arguments
@@ -47,46 +55,47 @@ class _ChangeNotificationSetting
/**
* Return the mutation return type
*
* @return NotificationTypeType
*/
public function type()
public function type() : NotificationTypeType
{
return \IPS\core\Api\GraphQL\TypeRegistry::notificationType();
}
/**
* Resolves this mutation
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : array
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
}
$pieces = explode('_', $args['extension']);
if( \count( $pieces ) !== 2 )
if( count( $pieces ) !== 2 )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
throw new SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
}
$extensions = \IPS\Application::load( $pieces[0] )->extensions('core', 'Notifications');
$extensions = Application::load( $pieces[0] )->extensions('core', 'Notifications');
if( !isset( $extensions[ $pieces[1] ] ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
throw new SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
}
$extensionOptions = \IPS\Notification::availableOptions( \IPS\Member::loggedIn(), $extensions[ $pieces[1] ] );
$extensionOptions = Notification::availableOptions( Member::loggedIn(), $extensions[ $pieces[1] ] );
if( !isset( $extensionOptions[ $args['type'] ] ) || $extensionOptions[ $args['type'] ]['type'] !== 'standard' ) // Only standard types supported right now
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_TYPE', 'GQL/0003/1', 403 );
throw new SafeException( 'INVALID_TYPE', 'GQL/0003/1', 403 );
}
$value = array();
@@ -98,7 +107,7 @@ class _ChangeNotificationSetting
// If the mutation is trying to change a setting...
if( isset( $args[ $type ] ) )
{
if( $args[ $type ] === TRUE && $typeSettings['editable'] == TRUE && !\in_array( $type, $extensionType['disabled'] ) )
if( $args[ $type ] === TRUE && $typeSettings['editable'] == TRUE && !in_array( $type, $extensionType['disabled'] ) )
{
$value[ $type ] = $type;
}
@@ -118,8 +127,8 @@ class _ChangeNotificationSetting
foreach ( $extensionType['notificationTypes'] as $notificationKey )
{
\IPS\Db::i()->insert( 'core_notification_preferences', array(
'member_id' => \IPS\Member::loggedIn()->member_id,
Db::i()->insert( 'core_notification_preferences', array(
'member_id' => Member::loggedIn()->member_id,
'notification_key' => $notificationKey,
'preference' => implode( ',', $value )
), TRUE );
@@ -136,8 +145,8 @@ class _ChangeNotificationSetting
}
$option = $options[ $method ];
$methods[ $method ]['default'] = isset( $option['default'] ) && \in_array( $method, $option['default'] );
$methods[ $method ]['disabled'] = isset( $option['disabled'] ) && \in_array( $method, $option['disabled'] );
$methods[ $method ]['default'] = isset( $option['default'] ) && in_array( $method, $option['default'] );
$methods[ $method ]['disabled'] = isset( $option['disabled'] ) && in_array( $method, $option['disabled'] );
$methods[ $method ]['member'] = isset( $value[ $method ] );
}
@@ -10,25 +10,32 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use Exception;
use GraphQL\Type\Definition\BooleanType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Db;
use IPS\File;
use IPS\Member;
use UnderflowException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Delete attachment mutation for GraphQL API
*/
class _DeleteAttachment
class DeleteAttachment
{
/*
* @brief Query description
*/
public static $description = "Delete an attachment";
public static string $description = "Delete an attachment";
/*
* Mutation arguments
@@ -46,8 +53,10 @@ class _DeleteAttachment
/**
* Return the mutation return type
*
* @return BooleanType
*/
public function type()
public function type() : BooleanType
{
return TypeRegistry::boolean();
}
@@ -55,21 +64,22 @@ class _DeleteAttachment
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return bool
*/
public function resolve($val, $args, $context, $info)
public function resolve( mixed $val, array $args, array $context, array $info ) : bool
{
/* Get the attachment */
try
{
$attachment = \IPS\Db::i()->select( '*', 'core_attachments', array( 'attach_id=?', $args['id'] ) )->first();
$attachment = Db::i()->select( '*', 'core_attachments', array( 'attach_id=?', $args['id'] ) )->first();
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_ATTACHMENT', 'GQL/0004/1', 404 );
throw new SafeException( 'NO_ATTACHMENT', 'GQL/0004/1', 404 );
}
/* Delete the maps - Only do this for attachments that have actually been saved (if they haven't been saved, there's nothing in core_attachments_map for us to delete */
@@ -85,34 +95,34 @@ class _DeleteAttachment
}
}
\IPS\Db::i()->delete( 'core_attachments_map', $where );
Db::i()->delete( 'core_attachments_map', $where );
}
else
{
/* If the attachment hasn't been claimed yet, it should only be deletable by the person who uploaded it */
if( $attachment['attach_member_id'] != \IPS\Member::loggedIn()->member_id )
if( $attachment['attach_member_id'] != Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'CANNOT_DELETE_OTHERS_ATTACHMENTS', 'GQL/0004/2', 403 );
throw new SafeException( 'CANNOT_DELETE_OTHERS_ATTACHMENTS', 'GQL/0004/2', 403 );
}
}
/* If there's no other maps, we can delete the attachment itself */
$otherMaps = \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments_map', array( 'attachment_id=?', $attachment['attach_id'] ) )->first();
$otherMaps = Db::i()->select( 'COUNT(*)', 'core_attachments_map', array( 'attachment_id=?', $attachment['attach_id'] ) )->first();
if ( !$otherMaps )
{
\IPS\Db::i()->delete( 'core_attachments', array( 'attach_id=?', $attachment['attach_id'] ) );
Db::i()->delete( 'core_attachments', array( 'attach_id=?', $attachment['attach_id'] ) );
try
{
\IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->delete();
File::get( 'core_Attachment', $attachment['attach_location'] )->delete();
}
catch ( \Exception $e ) { }
catch ( Exception $e ) { }
if ( $attachment['attach_thumb_location'] )
{
try
{
\IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->delete();
File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->delete();
}
catch ( \Exception $e ) { }
catch ( Exception $e ) { }
}
}
@@ -10,25 +10,40 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use InvalidArgumentException;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Api\GraphQL\Types\FollowType;
use IPS\Application;
use IPS\Content;
use IPS\core\Followed\Follow as FollowClass;
use IPS\Db;
use IPS\IPS;
use IPS\Member;
use IPS\Node\Model;
use IPS\Notification;
use OutOfRangeException;
use UnderflowException;
use function defined;
use function get_class;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Follow something mutation for GraphQL API
*/
class _Follow
class Follow
{
/*
* @brief Query description
*/
public static $description = "Follow a node, item or member";
public static string $description = "Follow a node, item or member";
/*
* Mutation arguments
@@ -50,8 +65,10 @@ class _Follow
/**
* Return the mutation return type
*
* @return FollowType
*/
public function type()
public function type() : FollowType
{
return TypeRegistry::follow();
}
@@ -61,46 +78,45 @@ class _Follow
* @todo this is basically copied and pasted from notifications.php which isn't ideal, so we
* might want to consider refactoring to abstract this functionality.
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : array
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0001/6', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0001/6', 403 );
}
/* Get class */
try
{
$class = \IPS\core\Followed\Follow::getClassToFollow( $args['app'], $args['area'] );
$class = FollowClass::getClassToFollow( $args['app'], $args['area'] );
}
catch( \InvalidArgumentException $e )
catch( InvalidArgumentException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_FOUND', 'GQL/0001/4', 404 );
throw new SafeException( 'NOT_FOUND', 'GQL/0001/4', 404 );
}
if( $args['app'] == 'core' and $args['area'] == 'member' )
{
/* You can't follow yourself */
if( $args['id'] == \IPS\Member::loggedIn()->member_id )
if( $args['id'] == Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'CANT_FOLLOW_SELF', 'GQL/0001/1', 403 );
throw new SafeException( 'CANT_FOLLOW_SELF', 'GQL/0001/1', 403 );
}
/* Following disabled */
$member = \IPS\Member::load( $args['id'] );
$member = Member::load( $args['id'] );
if( !$member->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'CANT_FOLLOW_MEMBER', 'GQL/0001/2', 403 );
throw new SafeException( 'CANT_FOLLOW_MEMBER', 'GQL/0001/2', 403 );
}
if( $member->members_bitoptions['pp_setting_moderate_followers'] and !\IPS\Member::loggedIn()->following( 'core', 'member', $member->member_id ) )
if( $member->members_bitoptions['pp_setting_moderate_followers'] and !Member::loggedIn()->following( 'core', 'member', $member->member_id ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'CANT_FOLLOW_MEMBER', 'GQL/0001/3', 403 );
throw new SafeException( 'CANT_FOLLOW_MEMBER', 'GQL/0001/3', 403 );
}
}
@@ -115,49 +131,54 @@ class _Follow
$thing = NULL;
try
{
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
if ( in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
/* @var Model $class */
$thing = $class::loadAndCheckPerms( (int) $args['id'] );
}
elseif ( $class == 'IPS\Member\Club' )
{
/* @var Member\Club $class */
$thing = $class::loadAndCheckPerms( (int) $args['id'] );
}
elseif ( $class != "IPS\Member" )
{
/* @var Content $class */
$thing = $class::loadAndCheckPerms( (int) $args['id'] );
}
else
{
if( !$class instanceof \IPS\Content\Followable )
if( !IPS::classUsesTrait( $class, 'IPS\Content\Followable' ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
/* @var Member $class */
$thing = $class::load( (int) $args['id'] );
}
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_FOUND', 'GQL/0001/5', 404 );
throw new SafeException( 'NOT_FOUND', 'GQL/0001/5', 404 );
}
/* Do we follow it? */
try
{
$current = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=? AND follow_member_id=?', $args['app'], $args['area'], $args['id'], \IPS\Member::loggedIn()->member_id ) )->first();
$current = Db::i()->select( '*', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=? AND follow_member_id=?', $args['app'], $args['area'], $args['id'], Member::loggedIn()->member_id ) )->first();
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
$current = FALSE;
}
/* Insert */
$save = array(
'follow_id' => md5( $args['app'] . ';' . $args['area'] . ';' . $args['id'] . ';' . \IPS\Member::loggedIn()->member_id ),
'follow_id' => md5( $args['app'] . ';' . $args['area'] . ';' . $args['id'] . ';' . Member::loggedIn()->member_id ),
'follow_app' => $args['app'],
'follow_area' => $args['area'],
'follow_rel_id' => $args['id'],
'follow_member_id' => \IPS\Member::loggedIn()->member_id,
'follow_member_id' => Member::loggedIn()->member_id,
'follow_is_anon' => $args['anonymous'],
'follow_added' => time(),
'follow_notify_do' => $args['type'] == 'NONE' ? 0 : 1,
@@ -168,11 +189,11 @@ class _Follow
);
if ( $current )
{
\IPS\Db::i()->update( 'core_follow', $save, array( 'follow_id=?', $current['follow_id'] ) );
Db::i()->update( 'core_follow', $save, array( 'follow_id=?', $current['follow_id'] ) );
}
else
{
\IPS\Db::i()->insert( 'core_follow', $save );
Db::i()->insert( 'core_follow', $save );
}
/* Also follow all nodes if following club */
@@ -185,11 +206,11 @@ class _Follow
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
$save = array(
'follow_id' => md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . \IPS\Member::loggedIn()->member_id ),
'follow_id' => md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . Member::loggedIn()->member_id ),
'follow_app' => $followApp,
'follow_area' => $followArea,
'follow_rel_id' => $node['node_id'],
'follow_member_id' => \IPS\Member::loggedIn()->member_id,
'follow_member_id' => Member::loggedIn()->member_id,
'follow_is_anon' => $args['anonymous'],
'follow_added' => time(),
'follow_notify_do' => $args['type'] == 'NONE' ? 0 : 1,
@@ -198,25 +219,25 @@ class _Follow
'follow_notify_sent' => 0,
'follow_visible' => 1,
);
\IPS\Db::i()->insert( 'core_follow', $save, TRUE );
Db::i()->insert( 'core_follow', $save, TRUE );
}
}
/* Send notification if following member */
if( $class == "IPS\Member" )
{
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'member_follow', \IPS\Member::loggedIn(), array( \IPS\Member::loggedIn() ) );
$notification = new Notification( Application::load( 'core' ), 'member_follow', Member::loggedIn(), array( Member::loggedIn() ) );
$notification->recipients->attach( $thing );
$notification->send();
}
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
if ( in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
$return = array_merge($return, array(
'node' => $thing,
'nodeClass' => \get_class( $thing )
'nodeClass' => get_class( $thing )
));
}
else if( $class == 'IPS\Member' )
@@ -233,7 +254,7 @@ class _Follow
{
$return = array_merge($return, array(
'item' => $thing,
'itemClass' => \get_class( $thing )
'itemClass' => get_class( $thing )
));
}
@@ -10,25 +10,31 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\IgnoreOptionType;
use IPS\core\Ignore;
use IPS\Member;
use OutOfRangeException;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Ignore user mutation for GraphQL API
*/
class _IgnoreUser
class IgnoreUser
{
/*
* @brief Query description
*/
public static $description = "Ignore a member";
public static string $description = "Ignore a member";
/*
* Mutation arguments
@@ -45,7 +51,7 @@ class _IgnoreUser
/**
* Return the mutation return type
*/
public function type()
public function type() : IgnoreOptionType
{
return \IPS\core\api\GraphQL\TypeRegistry::ignoreOption();
}
@@ -53,47 +59,46 @@ class _IgnoreUser
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : array
{
if ( !\in_array( $args['type'], \IPS\core\Ignore::types() ) )
if ( !in_array( $args['type'], Ignore::types() ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_TYPE', 'GQL/0006/1', 404 );
throw new SafeException( 'INVALID_TYPE', 'GQL/0006/1', 404 );
}
$type = $args['type'];
$member = \IPS\Member::load( $args['member'] );
$member = Member::load( $args['member'] );
if( !$member->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_MEMBER', 'GQL/0006/2', 403 );
throw new SafeException( 'INVALID_MEMBER', 'GQL/0006/2', 403 );
}
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
if ( $member->member_id == Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_IGNORE_SELF', 'GQL/0006/3', 403 );
throw new SafeException( 'NO_IGNORE_SELF', 'GQL/0006/3', 403 );
}
if ( !$member->canBeIgnored() )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_IGNORE_MEMBER', 'GQL/0006/4', 403 );
throw new SafeException( 'NO_IGNORE_MEMBER', 'GQL/0006/4', 403 );
}
try
{
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
$ignore->$type = $args['isIgnoring'];
$ignore->save();
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$ignore = new \IPS\core\Ignore;
$ignore = new Ignore;
$ignore->$type = $args['isIgnoring'];
$ignore->owner_id = \IPS\Member::loggedIn()->member_id;
$ignore->owner_id = Member::loggedIn()->member_id;
$ignore->ignore_id = $member->member_id;
$ignore->save();
}
@@ -103,8 +108,8 @@ class _IgnoreUser
'is_being_ignored' => $args['isIgnoring']
);
\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();
return $return;
}
@@ -10,25 +10,33 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\core\api\GraphQL\Types\NotificationType;
use IPS\Db;
use IPS\Member;
use IPS\Notification\Api;
use UnderflowException;
use function defined;
use function intval;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Mark notification read mutation for GraphQL API
*/
class _MarkNotificationRead
class MarkNotificationRead
{
/*
* @brief Query description
*/
public static $description = "Mark a notification as read";
public static string $description = "Mark a notification as read";
/*
* Mutation arguments
@@ -43,7 +51,7 @@ class _MarkNotificationRead
/**
* Return the mutation return type
*/
public function type()
public function type() : NotificationType
{
return \IPS\core\Api\GraphQL\TypeRegistry::notification();
}
@@ -51,45 +59,44 @@ class _MarkNotificationRead
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array|NULL
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : ?array
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
}
$where = array();
$where[] = array( "notification_app IN('" . implode( "','", array_keys( \IPS\Application::enabledApplications() ) ) . "')" );
$where[] = array( "member = ?", \IPS\Member::loggedIn()->member_id );
$where[] = array( "notification_app IN('" . implode( "','", array_keys( Application::enabledApplications() ) ) . "')" );
$where[] = array( "member = ?", Member::loggedIn()->member_id );
if( isset( $args['id'] ) && $args['id'] !== NULL )
if( isset( $args['id'] ) )
{
$where[] = array( "id = ?", \intval( $args['id'] ) );
$where[] = array( "id = ?", intval( $args['id'] ) );
}
try
{
$row = \IPS\Db::i()->select( '*', 'core_notifications', $where )->first();
$notification = \IPS\Notification\Api::constructFromData( $row );
$row = Db::i()->select( '*', 'core_notifications', $where )->first();
$notification = Api::constructFromData( $row );
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
if( isset( $args['id'] ) && $args['id'] !== NULL )
if( isset( $args['id'] ) )
{
// Only throw an error if we were trying to work on a specific notification
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_NOTIFICATION', 'GQL/0003/2', 403 );
throw new SafeException( 'INVALID_NOTIFICATION', 'GQL/0003/2', 403 );
}
}
\IPS\Db::i()->update( 'core_notifications', array( 'read_time' => time() ), $where );
\IPS\Member::loggedIn()->recountNotifications();
Db::i()->update( 'core_notifications', array( 'read_time' => time() ), $where );
Member::loggedIn()->recountNotifications();
if( $notification )
if( isset( $notification ) )
{
return array( 'notification' => $notification, 'data' => $notification->getData() );
}
@@ -10,25 +10,33 @@
*/
namespace IPS\core\api\GraphQL\Mutations\Messenger;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\Application\Module;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\core\Messenger\Conversation;
use IPS\Member;
use IPS\Notification;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Add user to conversation mutation for GraphQL API
*/
class _AddConversationUser
class AddConversationUser
{
/*
* @brief Query description
*/
public static $description = "Add a user to a PM conversation";
public static string $description = "Add a user to a PM conversation";
/*
* Mutation arguments
@@ -44,7 +52,7 @@ class _AddConversationUser
/**
* Return the mutation return type
*/
public function type()
public function type() : MessengerConversationType
{
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
}
@@ -52,38 +60,37 @@ class _AddConversationUser
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\forums\Forum
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return Conversation
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : Conversation
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
}
try
{
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
$conversation = Conversation::loadAndCheckPerms( $args['id'] );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2_graphql', 400 );
throw new SafeException( 'NO_MESSAGE', '1F294/2_graphql', 400 );
}
// Fetch our new member
$member = \IPS\Member::load( $args['memberId'] );
$member = Member::load( $args['memberId'] );
if( !$member->member_id ){
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_MEMBER', '1F294/2_graphql', 403 );
throw new SafeException( 'INVALID_MEMBER', '1F294/2_graphql', 403 );
}
// Check member limit
if ( \IPS\Member::loggedIn()->group['g_max_mass_pm'] !== -1 AND $conversation->to_count + 1 > \IPS\Member::loggedIn()->group['g_max_mass_pm'] )
if ( Member::loggedIn()->group['g_max_mass_pm'] !== -1 AND $conversation->to_count + 1 > Member::loggedIn()->group['g_max_mass_pm'] )
{
throw new \IPS\Api\GraphQL\SafeException( 'TOO_MANY_RECIPIENTS', '1F294/2', 400 );
throw new SafeException( 'TOO_MANY_RECIPIENTS', '1F294/2', 400 );
}
$maps = $conversation->maps( TRUE );
@@ -91,12 +98,12 @@ class _AddConversationUser
// Authorize the member
if ( array_key_exists( $member->member_id, $maps ) and !$maps[ $member->member_id ]['map_user_active'] AND !$maps[ $member->member_id ]['map_user_banned'] )
{
throw new \IPS\Api\GraphQL\SafeException( 'MEMBER_LEFT', '1F294/2', 403 );
throw new SafeException( 'MEMBER_LEFT', '1F294/2', 403 );
}
if ( !$this->memberCanReceiveNewMessage( $member ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'BAD_RECIPIENT', '1F294/2', 403 );
throw new SafeException( 'BAD_RECIPIENT', '1F294/2', 403 );
}
//echo 'here';
@@ -104,7 +111,7 @@ class _AddConversationUser
$conversation->authorize( $member );
$conversation->maps(TRUE); // Need to rebuild participant maps here to reflect this change
$notification = new \IPS\Notification( \IPS\Application::load('core'), 'private_message_added', $conversation, array( $conversation, \IPS\Member::loggedIn() ) );
$notification = new Notification( Application::load('core'), 'private_message_added', $conversation, array( $conversation, Member::loggedIn() ) );
$notification->recipients->attach( $member );
$notification->send();
@@ -114,10 +121,10 @@ class _AddConversationUser
/**
* Can the member recieve new messages?
*
* @param \IPS\Member The member to check
* @return boolean
* @param Member $member The member to check
* @return bool
*/
public function memberCanReceiveNewMessage( $member )
public function memberCanReceiveNewMessage( Member $member ) : bool
{
if ( $member->members_disable_pm )
{
@@ -125,19 +132,19 @@ class _AddConversationUser
}
/* Group can not use messenger */
if ( !$member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) )
if ( !$member->canAccessModule( Module::get( 'core', 'messaging', 'front' ) ) )
{
return FALSE;
}
/* Inbox is full */
if ( ( $member->group['g_max_messages'] > 0 AND $member->msg_count_total >= $member->group['g_max_messages'] ) and !\IPS\Member::loggedIn()->group['gbw_pm_override_inbox_full'] )
if ( ( $member->group['g_max_messages'] > 0 AND $member->msg_count_total >= $member->group['g_max_messages'] ) and !Member::loggedIn()->group['gbw_pm_override_inbox_full'] )
{
return FALSE;
}
/* Is being ignored */
if ( $member->isIgnoring( \IPS\Member::loggedIn(), 'messages' ) )
if ( $member->isIgnoring( Member::loggedIn(), 'messages' ) )
{
return FALSE;
}
@@ -10,25 +10,30 @@
*/
namespace IPS\core\api\GraphQL\Mutations\Messenger;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\core\Messenger\Conversation;
use IPS\Member;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Leave conversation mutation for GraphQL API
*/
class _LeaveConversation
class LeaveConversation
{
/*
* @brief Query description
*/
public static $description = "Leave a PM conversation";
public static string $description = "Leave a PM conversation";
/*
* Mutation arguments
@@ -43,7 +48,7 @@ class _LeaveConversation
/**
* Return the mutation return type
*/
public function type()
public function type() : MessengerConversationType
{
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
}
@@ -51,26 +56,25 @@ class _LeaveConversation
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\forums\Forum
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return mixed
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : mixed
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
}
try
{
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
$conversation->deauthorize( \IPS\Member::loggedIn() );
$conversation = Conversation::loadAndCheckPerms( $args['id'] );
$conversation->deauthorize( Member::loggedIn() );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2_graphl', 400 );
throw new SafeException( 'NO_MESSAGE', '1F294/2_graphl', 400 );
}
return NULL;
@@ -10,25 +10,30 @@
*/
namespace IPS\core\api\GraphQL\Mutations\Messenger;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\core\Messenger\Conversation;
use IPS\Member;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Remove user from conversation mutation for GraphQL API
*/
class _RemoveConversationUser
class RemoveConversationUser
{
/*
* @brief Query description
*/
public static $description = "Leave a PM conversation";
public static string $description = "Leave a PM conversation";
/*
* Mutation arguments
@@ -44,7 +49,7 @@ class _RemoveConversationUser
/**
* Return the mutation return type
*/
public function type()
public function type() : MessengerConversationType
{
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
}
@@ -52,33 +57,32 @@ class _RemoveConversationUser
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\forums\Forum
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return Conversation
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : Conversation
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
}
try
{
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
$conversation = Conversation::loadAndCheckPerms( $args['id'] );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2_graphl', 400 );
throw new SafeException( 'NO_MESSAGE', '1F294/2_graphl', 400 );
}
if( $conversation->starter_id !== \IPS\Member::loggedIn()->member_id )
if( $conversation->starter_id !== Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_OWNER', 'GQL/0003/1', 403 );
throw new SafeException( 'NOT_OWNER', 'GQL/0003/1', 403 );
}
$conversation->deauthorize( \IPS\Member::load( $args['memberId'] ), TRUE );
$conversation->deauthorize( Member::load( $args['memberId'] ), TRUE );
$conversation->maps(TRUE); // Need to rebuild participant maps here to reflect this change
return $conversation;
@@ -10,25 +10,34 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Api\GraphQL\Types\FollowType;
use IPS\Application;
use IPS\Db;
use IPS\IPS;
use IPS\Member;
use OutOfRangeException;
use function defined;
use function get_class;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Follow something mutation for GraphQL API
*/
class _Unfollow
class Unfollow
{
/*
* @brief Query description
*/
public static $description = "Unfollow a node, item or member";
public static string $description = "Unfollow a node, item or member";
/*
* Mutation arguments
@@ -46,7 +55,7 @@ class _Unfollow
/**
* Return the mutation return type
*/
public function type()
public function type() : FollowType
{
return TypeRegistry::follow();
}
@@ -56,25 +65,27 @@ class _Unfollow
* @todo this is basically copied and pasted from notifications.php which isn't ideal, so we
* might want to consider refactoring to abstract this functionality.
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : array
{
if( !\IPS\Member::loggedIn()->member_id )
if( !Member::loggedIn()->member_id )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0002/6', 403 );
throw new SafeException( 'NOT_LOGGED_IN', 'GQL/0002/6', 403 );
}
try
{
$follow = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_id=? AND follow_member_id=?', $args['followID'], \IPS\Member::loggedIn()->member_id ) )->first();
$follow = Db::i()->select( '*', 'core_follow', array( 'follow_id=? AND follow_member_id=?', $args['followID'], Member::loggedIn()->member_id ) )->first();
}
catch ( OutOfRangeException $e )
{
throw new SafeException( 'NOT_FOUND', 'GQL/0002/7', 404 );
}
catch ( \OutOfRangeException $e ) {}
\IPS\Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', $args['followID'], \IPS\Member::loggedIn()->member_id ) );
Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', $args['followID'], Member::loggedIn()->member_id ) );
/* Get class */
if( $args['app'] == 'core' and $args['area'] == 'member' )
@@ -88,16 +99,16 @@ class _Unfollow
else
{
$class = NULL;
foreach ( \IPS\Application::load( $args['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
foreach ( Application::load( $args['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
{
foreach ( $ext->classes as $classname )
{
if ( $classname == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
if ( $classname == 'IPS\\' . $args['app'] . '\\' . IPS::mb_ucfirst( $args['area'] ) )
{
$class = $classname;
break;
}
if ( isset( $classname::$containerNodeClass ) and $classname::$containerNodeClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
if ( isset( $classname::$containerNodeClass ) and $classname::$containerNodeClass == 'IPS\\' . $args['app'] . '\\' . IPS::mb_ucfirst( $args['area'] ) )
{
$class = $classname::$containerNodeClass;
break;
@@ -106,7 +117,7 @@ class _Unfollow
{
foreach( $classname::$containerFollowClasses as $followClass )
{
if( $followClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
if( $followClass == 'IPS\\' . $args['app'] . '\\' . IPS::mb_ucfirst( $args['area'] ) )
{
$class = $followClass;
break;
@@ -117,9 +128,9 @@ class _Unfollow
}
}
if ( !$class or !array_key_exists( $args['app'], \IPS\Application::applications() ) )
if ( !$class or !array_key_exists( $args['app'], Application::applications() ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_FOUND', 'GQL/0001/7', 404 );
throw new SafeException( 'NOT_FOUND', 'GQL/0001/7', 404 );
}
/* Get our return info ready */
@@ -152,15 +163,15 @@ class _Unfollow
$followApp = $itemClass::$application;
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
\IPS\Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . \IPS\Member::loggedIn()->member_id ), \IPS\Member::loggedIn()->member_id ) );
Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . Member::loggedIn()->member_id ), Member::loggedIn()->member_id ) );
}
}
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
if ( in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
$return = array_merge($return, array(
'node' => $thing,
'nodeClass' => \get_class( $thing )
'nodeClass' => get_class( $thing )
));
}
else if( $class == 'IPS\Member' )
@@ -177,11 +188,11 @@ class _Unfollow
{
$return = array_merge($return, array(
'item' => $thing,
'itemClass' => \get_class( $thing )
'itemClass' => get_class( $thing )
));
}
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
}
@@ -10,26 +10,34 @@
*/
namespace IPS\core\api\GraphQL\Mutations;
use GraphQL\Type\Definition\ObjectType;
use Exception;
use GraphQL\Type\Definition\UnionType;
use InvalidArgumentException;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\File;
use IPS\Helpers\Form\Editor;
use IPS\Image;
use IPS\Member;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Upload attachment mutation for GraphQL API
*/
class _UploadAttachment
class UploadAttachment
{
/*
* @brief Query description
*/
public static $description = "Upload a file to be an attachment";
public static string $description = "Upload a file to be an attachment";
/*
* Mutation arguments
@@ -49,7 +57,7 @@ class _UploadAttachment
/**
* Return the mutation return type
*/
public function type()
public function type() : UnionType
{
return new UnionType([
'name' => 'core_UploadAttachment',
@@ -73,23 +81,24 @@ class _UploadAttachment
/**
* Resolves this mutation
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return array
*/
public function resolve($val, $args, $context, $info)
public function resolve( mixed $val, array $args, array $context, array $info ) : array
{
$storageClass = \IPS\File::getClass( 'core_Attachment' );
$storageClass = File::getClass( 'core_Attachment' );
$contents = base64_decode( $args['contents'] );
/* Check allowed types */
$ext = mb_substr( $args['name'], mb_strrpos( $args['name'], '.' ) + 1 );
if( $allowedFileTypes = \IPS\Helpers\Form\Editor::allowedFileExtensions() )
if( $allowedFileTypes = Editor::allowedFileExtensions() )
{
if( !\in_array( mb_strtolower( $ext ), array_map( 'mb_strtolower', $allowedFileTypes ) ) )
if( !in_array( mb_strtolower( $ext ), array_map( 'mb_strtolower', $allowedFileTypes ) ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'FILE_TYPE_NOT_ALLOWED', '2C399/1_graphql', 403 );
throw new SafeException( 'FILE_TYPE_NOT_ALLOWED', '2C399/1_graphql', 403 );
}
}
@@ -115,15 +124,15 @@ class _UploadAttachment
$file = $storageClass->chunkFinish( $ref, 'core_Attachment' );
/* If it's got an image extension, check it's actually a valid image */
if ( \in_array( $ext, \IPS\Image::supportedExtensions() ) )
if ( in_array( $ext, Image::supportedExtensions() ) )
{
try
{
$file->getImageDimensions();
}
catch ( \Exception $e )
catch ( Exception $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NOT_VALID_IMAGE', '2C399/2_graphql', 403 );
throw new SafeException( 'NOT_VALID_IMAGE', '2C399/2_graphql', 403 );
}
}
}
@@ -141,31 +150,30 @@ class _UploadAttachment
else
{
/* If it's got an image extension, check it's actually a valid image */
if ( \in_array( $ext, \IPS\Image::supportedExtensions() ) )
if ( in_array( $ext, Image::supportedExtensions() ) )
{
try
{
$image = \IPS\Image::create( $contents );
$image = Image::create( $contents );
}
catch ( \InvalidArgumentException $e )
catch ( InvalidArgumentException $e )
{
throw new \IPS\Api\GraphQL\SafeException( $e->getMessage(), '2C399/2_graphql', 403 );
throw new SafeException( $e->getMessage(), '2C399/2_graphql', 403 );
}
}
/* Create the file */
try
{
$file = \IPS\File::create( 'core_Attachment', $args['name'], $contents );
$file = File::create( 'core_Attachment', $args['name'], $contents );
}
catch ( \Exception $e )
catch ( Exception $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'FILE_CREATION_FAILED', '2C399/3_graphql', 403 );
throw new SafeException( 'FILE_CREATION_FAILED', '2C399/3_graphql', 403 );
}
}
/* Make it into an attachment */
$attachment = $file->makeAttachment( $args['postKey'], \IPS\Member::loggedIn() );
return $attachment;
return $file->makeAttachment( $args['postKey'], Member::loggedIn() );
}
}
@@ -10,25 +10,28 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application\Module;
use IPS\core\api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\ActiveUsersType;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* OurPicks query for GraphQL API
*/
class _ActiveUsers
class ActiveUsers
{
/*
* @brief Query description
*/
public static $description = "Returns active user information";
public static string $description = "Returns active user information";
/*
* Query arguments
@@ -41,20 +44,20 @@ class _ActiveUsers
/**
* Return the query return type
*/
public function type()
public function type() : ActiveUsersType
{
return \IPS\core\api\GraphQL\TypeRegistry::activeUsers();
return TypeRegistry::activeUsers();
}
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @return mixed
*/
public function resolve($val)
public function resolve( mixed $val ) : mixed
{
if ( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'online', 'front' ) ) )
if ( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'online', 'front' ) ) )
{
return NULL;
}
+14 -12
View File
@@ -10,25 +10,27 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\ClubType;
use IPS\Member\Club as ClubClass;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Club query for GraphQL API
*/
class _Club
class Club
{
/*
* @brief Query description
*/
public static $description = "Returns a club";
public static string $description = "Returns a club";
/*
* Query arguments
@@ -42,7 +44,7 @@ class _Club
/**
* Return the query return type
*/
public function type()
public function type() : ClubType
{
return \IPS\core\api\GraphQL\TypeRegistry::club();
}
@@ -50,13 +52,13 @@ class _Club
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member\Club
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return ClubClass
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : ClubClass
{
return \IPS\Member\Club::loadAndCheckPerms( $args['id'] );
return ClubClass::loadAndCheckPerms( $args['id'] );
}
}
+22 -16
View File
@@ -10,20 +10,26 @@
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\ClubType;
use IPS\Db;
use IPS\Patterns\ActiveRecordIterator;
use function defined;
use function in_array;
use function is_int;
/* To prevent PHP errors (extending class does not exist) revealing path */
if( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER[ 'SERVER_PROTOCOL' ] ) ? $_SERVER[ 'SERVER_PROTOCOL' ] : 'HTTP/1.0' ).' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ).' 403 Forbidden' );
exit;
}
/**
* Club query for GraphQL API
*/
class _Clubs
class Clubs
{
/*
* @brief Query description
@@ -49,7 +55,7 @@ class _Clubs
'type' => TypeRegistry::eNum( [
'name' => 'clubs_order_by',
'description' => 'Fields on which topics can be sorted',
'values' => \IPS\core\api\GraphQL\Types\ClubType::getOrderByOptions()
'values' => ClubType::getOrderByOptions()
] ),
'defaultValue' => NULL // will use default sort option
],
@@ -70,7 +76,7 @@ class _Clubs
/**
* Return the query return type
*/
public function type(): \GraphQL\Type\Definition\ListOfType
public function type(): ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::club() );
}
@@ -78,19 +84,19 @@ class _Clubs
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Patterns\ActiveRecordIterator[\IPS\Member\Club]
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return ActiveRecordIterator[\IPS\Member\Club]
*/
public function resolve( mixed $val, array $args, array $context ): \IPS\Patterns\ActiveRecordIterator
public function resolve( mixed $val, array $args, array $context ): ActiveRecordIterator
{
$where = [];
$sortBy = ( isset( $args['orderBy'] ) and \in_array( $args['orderBy'], \IPS\core\api\GraphQL\Types\ClubType::getOrderByOptions() ) ) ? $args['orderBy'] : 'name';
$sortDir = ( isset( $args['orderDir'] ) and \in_array( mb_strtolower( $args['orderDir'] ), array( 'asc', 'desc' ) ) ) ? $args['orderDir'] : 'desc';
$limit =( isset( $args['orderDir'] ) and \is_int( $args['limit'] ) ) ? $args['limit'] : 25;
$sortBy = ( isset( $args['orderBy'] ) and in_array( $args['orderBy'], ClubType::getOrderByOptions() ) ) ? $args['orderBy'] : 'name';
$sortDir = ( isset( $args['orderDir'] ) and in_array( mb_strtolower( $args['orderDir'] ), array( 'asc', 'desc' ) ) ) ? $args['orderDir'] : 'desc';
$limit =( isset( $args['orderDir'] ) and is_int( $args['limit'] ) ) ? $args['limit'] : 25;
$query = \IPS\Db::i()->select( '*', 'core_clubs', $where, "{$sortBy} {$sortDir}", $limit );
return new \IPS\Patterns\ActiveRecordIterator( $query, '\IPS\Member\Club' );
$query = Db::i()->select( '*', 'core_clubs', $where, "{$sortBy} {$sortDir}", $limit );
return new ActiveRecordIterator( $query, '\IPS\Member\Club' );
}
}
@@ -10,26 +10,32 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use Exception;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\Content as ContentClass;
use IPS\Content\Api\GraphQL\ContentType;
use IPS\Http\Url;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Me query for GraphQL API
*/
class _Content
class Content
{
/*
* @brief Query description
*/
public static $description = "Return a generic piece of content";
public static string $description = "Return a generic piece of content";
/*
* Query arguments
@@ -44,7 +50,7 @@ class _Content
/**
* Return the query return type
*/
public function type()
public function type() : ContentType
{
return \IPS\Content\Api\GraphQL\TypeRegistry::content();
}
@@ -52,21 +58,20 @@ class _Content
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member|null
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return ContentClass|null
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : ?ContentClass
{
$url = \IPS\Http\Url::createFromString( $args['url'] );
$url = Url::createFromString( $args['url'] );
if ( !isset( $url->hiddenQueryString['app'] ) )
{
throw new Exception('Not a valid URL');
}
foreach ( \IPS\Application::load( $url->hiddenQueryString['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
foreach ( Application::load( $url->hiddenQueryString['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
{
foreach ( $ext->classes as $class )
{
@@ -85,7 +90,7 @@ class _Content
$object = $reviewClass::load( $url->queryString['review'] );
}
if ( !$object->canView( \IPS\Member::loggedIn() ) )
if ( !$object->canView( Member::loggedIn() ) )
{
// No permission
return NULL;
@@ -93,7 +98,7 @@ class _Content
return $object;
}
catch (\Exception $e)
catch (Exception $e)
{}
}
}
+18 -14
View File
@@ -10,26 +10,30 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\GroupType;
use IPS\Member\Group as GroupClass;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Group query for GraphQL API
*/
class _Group
class Group
{
/*
* @brief Query description
*/
public static $description = "Returns a member group";
public static string $description = "Returns a member group";
/*
* @brief Query arguments
@@ -44,7 +48,7 @@ class _Group
/**
* Return the query return type
*/
public function type()
public function type() : GroupType
{
return \IPS\core\api\GraphQL\TypeRegistry::group();
}
@@ -52,20 +56,20 @@ class _Group
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member\Group
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return GroupClass
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : GroupClass
{
try
{
return \IPS\Member\Group::load( $args['id'] );
return GroupClass::load( $args['id'] );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_GROUP', '1invalid_group_graph', 400 );
throw new SafeException( 'INVALID_GROUP', '1invalid_group_graph', 400 );
}
}
}
@@ -10,26 +10,29 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\LanguageType;
use IPS\Lang;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Me query for GraphQL API
*/
class _Language
class Language
{
/*
* @brief Query description
*/
public static $description = "Return language data";
public static string $description = "Return language data";
/*
* Query arguments
@@ -44,7 +47,7 @@ class _Language
/**
* Return the query return type
*/
public function type()
public function type() : LanguageType
{
return \IPS\core\api\GraphQL\TypeRegistry::language();
}
@@ -52,17 +55,16 @@ class _Language
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member|null
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return Lang
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : Lang
{
if( isset( $args['id'] ) ){
return \IPS\Lang::load( $args['id'] );
return Lang::load( $args['id'] );
}
return \IPS\Member::loggedIn()->language();
return Member::loggedIn()->language();
}
}
@@ -10,26 +10,29 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\LoginType;
use IPS\Login;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Login handler query for GraphQL API
*/
class _LoginHandlers
class LoginHandlers
{
/*
* @brief Query description
*/
public static $description = "Return login handler data";
public static string $description = "Return login handler data";
/*
* Query arguments
@@ -43,8 +46,10 @@ class _LoginHandlers
/**
* Return the query return type
*
* @return ListOfType<LoginType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::login() );
}
@@ -52,14 +57,13 @@ class _LoginHandlers
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member|null
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @return array
*/
public function resolve($val, $args)
public function resolve( mixed $val, array $args ) : array
{
$login = new \IPS\Login;
$login = new Login;
return $login->buttonMethods();
}
}
+16 -14
View File
@@ -10,26 +10,28 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MemberType;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Me query for GraphQL API
*/
class _Me
class Me
{
/*
* @brief Query description
*/
public static $description = "Return the logged-in member";
public static string $description = "Return the logged-in member";
/*
* Query arguments
@@ -42,21 +44,21 @@ class _Me
/**
* Return the query return type
*/
public function type()
public function type() : MemberType
{
return \IPS\core\api\GraphQL\TypeRegistry::member();
return TypeRegistry::member();
}
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member|null
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return Member
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : Member
{
return \IPS\Member::loggedIn();
return Member::loggedIn();
}
}
@@ -10,26 +10,28 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MemberType;
use IPS\Member as MemberClass;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Member query for GraphQL API
*/
class _Member
class Member
{
/*
* @brief Query description
*/
public static $description = "Return a member";
public static string $description = "Return a member";
/*
* Query arguments
@@ -45,7 +47,7 @@ class _Member
/**
* Return the query return type
*/
public function type()
public function type() : MemberType
{
return \IPS\core\api\GraphQL\TypeRegistry::member();
}
@@ -53,17 +55,19 @@ class _Member
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Member|null
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return MemberClass|null
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : ?MemberClass
{
if( isset( $args['loggedIn'] ) ){
return \IPS\Member::loggedIn();
return MemberClass::loggedIn();
} elseif ( isset( $args['id'] ) ){
return \IPS\Member::load( $args['id'] );
return MemberClass::load( $args['id'] );
}
return null;
}
}
@@ -11,25 +11,31 @@
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MemberType;
use IPS\Db;
use IPS\Patterns\ActiveRecordIterator;
use function defined;
use function in_array;
use function is_int;
/* To prevent PHP errors (extending class does not exist) revealing path */
if( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER[ 'SERVER_PROTOCOL' ] ) ? $_SERVER[ 'SERVER_PROTOCOL' ] : 'HTTP/1.0' ).' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ).' 403 Forbidden' );
exit;
}
/**
* Members query for GraphQL API
*/
class _Members
class Members
{
/*
* @brief Query description
*/
public static $description = 'Returns a list of members';
public static string $description = 'Returns a list of members';
/*
* Query arguments
@@ -50,7 +56,7 @@ class _Members
'type' => TypeRegistry::eNum( [
'name' => 'member_order_by',
'description' => 'Fields on which topics can be sorted',
'values' => \IPS\core\api\GraphQL\Types\MemberType::getOrderByOptions()
'values' => MemberType::getOrderByOptions()
] ),
'defaultValue' => NULL // will use default sort option
],
@@ -68,8 +74,10 @@ class _Members
/**
* Return the query return type
*
* @return ListOfType<MemberType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::member() );
}
@@ -77,19 +85,20 @@ class _Members
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return [\IPS\Members]
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return ActiveRecordIterator
*/
public function resolve( $val, $args, $context, $info )
public function resolve( mixed $val, array $args, array $context, array $info ) : ActiveRecordIterator
{
$where = [];
$sortBy = ( isset( $args['orderBy'] ) and \in_array( $args['orderBy'], \IPS\core\api\GraphQL\Types\MemberType::getOrderByOptions() ) ) ? $args['orderBy'] : 'member_id';
$sortDir = ( isset( $args['orderDir'] ) and \in_array( mb_strtolower( $args['orderDir'] ), array( 'asc', 'desc' ) ) ) ? $args['orderDir'] : 'desc';
$limit =( isset( $args['orderDir'] ) and \is_int( $args['limit'] ) ) ? $args['limit'] : 25;
$sortBy = ( isset( $args['orderBy'] ) and in_array( $args['orderBy'], MemberType::getOrderByOptions() ) ) ? $args['orderBy'] : 'member_id';
$sortDir = ( isset( $args['orderDir'] ) and in_array( mb_strtolower( $args['orderDir'] ), array( 'asc', 'desc' ) ) ) ? $args['orderDir'] : 'desc';
$limit =( isset( $args['orderDir'] ) and is_int( $args['limit'] ) ) ? $args['limit'] : 25;
$query = \IPS\Db::i()->select( '*', 'core_members', $where, "{$sortBy} {$sortDir}", $limit );
return new \IPS\Patterns\ActiveRecordIterator( $query, 'IPS\Member' );
$query = Db::i()->select( '*', 'core_members', $where, "{$sortBy} {$sortDir}", $limit );
return new ActiveRecordIterator( $query, 'IPS\Member' );
}
}
@@ -10,25 +10,29 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\core\Messenger\Conversation;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Messenger conversation query for GraphQL API
*/
class _MessengerConversation
class MessengerConversation
{
/*
* @brief Query description
*/
public static $description = "Returns a messenger conversation";
public static string $description = "Returns a messenger conversation";
/*
* Query arguments
@@ -43,7 +47,7 @@ class _MessengerConversation
/**
* Return the query return type
*/
public function type()
public function type() : MessengerConversationType
{
return \IPS\core\api\GraphQL\TypeRegistry::messengerConversation();
}
@@ -51,20 +55,21 @@ class _MessengerConversation
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Messenger\Conversation
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return Conversation
*/
public function resolve($val, $args, $context, $info)
public function resolve( mixed $val, array $args, array $context, array $info ) : Conversation
{
try
{
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
$conversation = Conversation::loadAndCheckPerms( $args['id'] );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2', 400 );
throw new SafeException( 'NO_MESSAGE', '1F294/2', 400 );
}
return $conversation;
@@ -10,25 +10,35 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Search\Mysql\Query;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\Db;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function is_null;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Messenger conversations query for GraphQL API
*/
class _MessengerConversations
class MessengerConversations
{
/*
* @brief Query description
*/
public static $description = "Returns a list of messenger conversations";
public static string $description = "Returns a list of messenger conversations";
/*
* Query arguments
@@ -66,7 +76,7 @@ class _MessengerConversations
'type' => TypeRegistry::eNum([
'name' => 'core_messengerConversations_orderBy',
'description' => 'Fields on which conversations can be sorted',
'values' => \IPS\core\api\GraphQL\Types\MessengerConversationType::getOrderByOptions()
'values' => MessengerConversationType::getOrderByOptions()
]),
'defaultValue' => 'last_post_time'
],
@@ -83,8 +93,10 @@ class _MessengerConversations
/**
* Return the query return type
*
* @return ListOfType<MessengerConversationType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerConversation() );
}
@@ -92,22 +104,23 @@ class _MessengerConversations
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Messenger\Conversation
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return ActiveRecordIterator|array
*/
public function resolve($val, $args, $context, $info)
public function resolve( mixed $val, array $args, array $context, array $info ) : ActiveRecordIterator|array
{
// Start to build the query to fetch messages
$where = array( array( 'core_message_topic_user_map.map_user_id=? AND core_message_topic_user_map.map_user_active=1', \IPS\Member::loggedIn()->member_id ) );
$where = array( array( 'core_message_topic_user_map.map_user_id=? AND core_message_topic_user_map.map_user_active=1', Member::loggedIn()->member_id ) );
$orderBy = 'mt_' . $args['orderBy'];
$offset = max( $args['offset'], 0 );
$limit = min( $args['limit'], 50 );
$iterator = NULL;
// Filters
if( !\is_null( $args['filter'] ) )
if( !is_null( $args['filter'] ) )
{
switch ($args['filter'] ) {
case "MINE":
@@ -126,7 +139,7 @@ class _MessengerConversations
}
// Folder
if ( !\is_null( $args['folder'] ) )
if ( !is_null( $args['folder'] ) )
{
$folderObj = \IPS\core\api\GraphQL\TypeRegistry::messengerFolder();
$folders = $folderObj->getMemberFolders();
@@ -138,33 +151,33 @@ class _MessengerConversations
}
// Search
if( !\is_null( $args['search'] ) )
if( !is_null( $args['search'] ) )
{
$subQuery = \IPS\Db::i()->select( 'map_topic_id', array( 'core_message_topic_user_map', 'core_message_topic_user_map' ), $where );
$subQuery = Db::i()->select( 'map_topic_id', array( 'core_message_topic_user_map', 'core_message_topic_user_map' ), $where );
$where = array( array( 'core_message_posts.msg_topic_id IN (?)', $subQuery ) );
$query = array();
$prefix = \IPS\Db::i()->prefix;
$prefix = Db::i()->prefix;
if ( !\is_null( $args['searchIn'] ) )
if ( !is_null( $args['searchIn'] ) )
{
if ( \in_array( 'TOPIC', $args['searchIn'] ) )
if ( in_array( 'TOPIC', $args['searchIn'] ) )
{
$query[] = \IPS\Content\Search\Mysql\Query::matchClause( "core_message_topics.mt_title", $args['search'], '+', FALSE );
$query[] = Query::matchClause( "core_message_topics.mt_title", $args['search'], '+', FALSE );
}
if ( \in_array( 'POST', $args['searchIn'] ) )
if ( in_array( 'POST', $args['searchIn'] ) )
{
$query[] = \IPS\Content\Search\Mysql\Query::matchClause( "core_message_posts.msg_post", $args['search'], '+', FALSE );
$query[] = Query::matchClause( "core_message_posts.msg_post", $args['search'], '+', FALSE );
}
if ( \in_array( 'RECIPIENT', $args['searchIn'] ) )
if ( in_array( 'RECIPIENT', $args['searchIn'] ) )
{
$query[] = "core_message_posts.msg_topic_id IN ( SELECT sender_map.map_topic_id FROM {$prefix}core_message_topic_user_map AS sender_map WHERE sender_map.map_is_starter=1 AND sender_map.map_user_id IN ( SELECT member_id FROM {$prefix}core_members AS sm WHERE name LIKE '" . \IPS\Db::i()->escape_string( $args['search'] ) . "%' ) )";
$query[] = "core_message_posts.msg_topic_id IN ( SELECT sender_map.map_topic_id FROM {$prefix}core_message_topic_user_map AS sender_map WHERE sender_map.map_is_starter=1 AND sender_map.map_user_id IN ( SELECT member_id FROM {$prefix}core_members AS sm WHERE name LIKE '" . Db::i()->escape_string( $args['search'] ) . "%' ) )";
}
if ( \in_array( 'SENDER', $args['searchIn'] ) )
if ( in_array( 'SENDER', $args['searchIn'] ) )
{
$query[] = "core_message_posts.msg_topic_id IN ( SELECT receiver_map.map_topic_id FROM {$prefix}core_message_topic_user_map AS receiver_map WHERE receiver_map.map_is_starter=0 AND receiver_map.map_user_id IN ( SELECT member_id FROM {$prefix}core_members AS rm WHERE name LIKE '" . \IPS\Db::i()->escape_string( $args['search'] ) . "%') )";
$query[] = "core_message_posts.msg_topic_id IN ( SELECT receiver_map.map_topic_id FROM {$prefix}core_message_topic_user_map AS receiver_map WHERE receiver_map.map_is_starter=0 AND receiver_map.map_user_id IN ( SELECT member_id FROM {$prefix}core_members AS rm WHERE name LIKE '" . Db::i()->escape_string( $args['search'] ) . "%') )";
}
if ( \count( $query ) )
if ( count( $query ) )
{
$where[] = array( '(' . implode( ' OR ', $query ) . ')' );
}
@@ -173,7 +186,7 @@ class _MessengerConversations
/* Get a count */
try
{
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_message_posts', $where, NULL, NULL, 'msg_topic_id' )
$count = Db::i()->select( 'COUNT(*)', 'core_message_posts', $where, NULL, NULL, 'msg_topic_id' )
->join(
'core_message_topics',
'core_message_posts.msg_topic_id=core_message_topics.mt_id'
@@ -184,7 +197,7 @@ class _MessengerConversations
)
->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$count = 0;
}
@@ -193,7 +206,7 @@ class _MessengerConversations
if( $count )
{
/* Because of strict group by, we first need to select the ids, then grab those topics */
$iterator = \IPS\Db::i()->select(
$iterator = Db::i()->select(
'core_message_posts.msg_topic_id',
'core_message_posts',
$where,
@@ -209,7 +222,7 @@ class _MessengerConversations
);
/* Get iterator */
$iterator = \IPS\Db::i()->select(
$iterator = Db::i()->select(
'core_message_topic_user_map.*, core_message_topics.*',
'core_message_topic_user_map',
array( 'map_topic_id IN(' . implode( ',', iterator_to_array( $iterator ) ) . ')' ),
@@ -223,7 +236,7 @@ class _MessengerConversations
else
{
/* Get iterator */
$iterator = \IPS\Db::i()->select(
$iterator = Db::i()->select(
'core_message_topic_user_map.*, core_message_topics.*',
'core_message_topic_user_map',
$where,
@@ -237,7 +250,7 @@ class _MessengerConversations
if( $iterator )
{
return new \IPS\Patterns\ActiveRecordIterator( $iterator, 'IPS\core\Messenger\Conversation' );
return new ActiveRecordIterator( $iterator, 'IPS\core\Messenger\Conversation' );
}
return array();
@@ -10,25 +10,28 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\MessengerFolderType;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Messenger folders query for GraphQL API
*/
class _MessengerFolders
class MessengerFolders
{
/*
* @brief Query description
*/
public static $description = "Returns the member's messenger folders";
public static string $description = "Returns the member's messenger folders";
/*
* Query arguments
@@ -42,8 +45,10 @@ class _MessengerFolders
/**
* Return the query return type
*
* @return ListOfType<MessengerFolderType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerFolder() );
}
@@ -51,14 +56,15 @@ class _MessengerFolders
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Messenger\Conversation
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @param array $info
* @return array|null
*/
public function resolve($val, $args, $context, $info)
public function resolve( mixed $val, array $args, array $context, array $info ) : ?array
{
if( !\IPS\Member::loggedIn()->member_id || \IPS\Member::loggedIn()->members_disable_pm )
if( !Member::loggedIn()->member_id || Member::loggedIn()->members_disable_pm )
{
return NULL;
}
@@ -10,25 +10,31 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\core\api\GraphQL\Types\NotificationTypeType;
use IPS\Member;
use IPS\Notification;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* NotificationTypes query for GraphQL API
*/
class _NotificationTypes
class NotificationTypes
{
/*
* @brief Query description
*/
public static $description = "Returns available notification types";
public static string $description = "Returns available notification types";
/*
* Query arguments
@@ -40,8 +46,10 @@ class _NotificationTypes
/**
* Return the query return type
*
* @return ListOfType<NotificationTypeType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::notificationType() );
}
@@ -49,23 +57,23 @@ class _NotificationTypes
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return array
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : array
{
//$defaultConfiguration = \IPS\Notification::defaultConfiguration();
//$memberConfiguration = $member->notificationsConfiguration();
$notificationTypes = array();
$extensions = \IPS\Application::allExtensions( 'core', 'Notifications' );
$extensions = Application::allExtensions( 'core', 'Notifications' );
$availableOptions = array();
foreach( $extensions as $extensionKey => $extension )
{
$extensionOptions = \IPS\Notification::availableOptions( \IPS\Member::loggedIn(), $extension );
$extensionOptions = Notification::availableOptions( Member::loggedIn(), $extension );
foreach( $extensionOptions as $key => $option )
{
@@ -84,9 +92,9 @@ class _NotificationTypes
continue;
}
$methods[ $method ]['default'] = isset( $option['default'] ) && \in_array( $method, $option['default'] );
$methods[ $method ]['disabled'] = isset( $option['disabled'] ) && \in_array( $method, $option['disabled'] );
$methods[ $method ]['member'] = isset( $option['options'][ $method ] ) && $option['options'][ $method ]['value'] == TRUE;
$methods[ $method ]['default'] = isset( $option['default'] ) && in_array( $method, $option['default'] );
$methods[ $method ]['disabled'] = isset( $option['disabled'] ) && in_array( $method, $option['disabled'] );
$methods[ $method ]['member'] = ( $option['options'][ $method ]['value'] == TRUE );
}
$notificationTypes[] = array(
@@ -10,25 +10,30 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\PromotedItemType;
use IPS\core\Feature;
use function count;
use function defined;
use function is_int;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* OurPicks query for GraphQL API
*/
class _OurPicks
class OurPicks
{
/*
* @brief Query description
*/
public static $description = "Returns promoted items";
public static string $description = "Returns promoted items";
/*
* Query arguments
@@ -42,8 +47,10 @@ class _OurPicks
/**
* Return the query return type
*
* @return ListOfType<PromotedItemType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::promotedItem() );
}
@@ -51,21 +58,21 @@ class _OurPicks
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return array|null
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : ?array
{
$limit = 7;
if( isset( $args['count'] ) && \is_int( $args['count'] ) )
if( isset( $args['count'] ) && is_int( $args['count'] ) )
{
$limit = $args['count'];
}
$items = \IPS\core\Promote::internalStream( $limit );
if( !\count( $items ) )
$items = Feature::internalStream( $limit );
if( !count( $items ) )
{
return NULL;
}
@@ -10,25 +10,31 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use DateInterval;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\PopularContributorType;
use IPS\DateTime;
use IPS\Db;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PopularContributors query for GraphQL API
*/
class _PopularContributors
class PopularContributors
{
/*
* @brief Query description
*/
public static $description = "Returns popular contributors";
public static string $description = "Returns popular contributors";
/*
* Query arguments
@@ -52,8 +58,10 @@ class _PopularContributors
/**
* Return the query return type
*
* @return ListOfType<PopularContributorType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::popularContributor() );
}
@@ -61,12 +69,12 @@ class _PopularContributors
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return array
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : array
{
/* How many? */
$limit = min( $args['limit'], 25 );
@@ -79,29 +87,29 @@ class _PopularContributors
switch ( $args['period'] )
{
case 'WEEK':
$where[] = array( 'rep_date>' . \IPS\DateTime::create()->sub( new \DateInterval( 'P1W' ) )->getTimestamp() );
$where[] = array( 'rep_date>' . DateTime::create()->sub( new DateInterval( 'P1W' ) )->getTimestamp() );
break;
case 'MONTH':
$where[] = array( 'rep_date>' . \IPS\DateTime::create()->sub( new \DateInterval( 'P1M' ) )->getTimestamp() );
$where[] = array( 'rep_date>' . DateTime::create()->sub( new DateInterval( 'P1M' ) )->getTimestamp() );
break;
case 'YEAR':
$where[] = array( 'rep_date>' . \IPS\DateTime::create()->sub( new \DateInterval( 'P1Y' ) )->getTimestamp() );
$where[] = array( 'rep_date>' . DateTime::create()->sub( new DateInterval( 'P1Y' ) )->getTimestamp() );
break;
}
$innerQuery = \IPS\Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', $where, NULL, NULL, 'member' );
$topContributors = iterator_to_array( \IPS\Db::i()->select( 'member, rep', array( $innerQuery, 'in' ), NULL, 'rep DESC', $limit )->setKeyField('member')->setValueField('rep') );
$innerQuery = Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', $where, NULL, NULL, 'member' );
$topContributors = iterator_to_array( Db::i()->select( 'member, rep', array( $innerQuery, 'in' ), NULL, 'rep DESC', $limit )->setKeyField('member')->setValueField('rep') );
}
else
{
$topContributors = iterator_to_array( \IPS\Db::i()->select( 'member_id as member, pp_reputation_points as rep', 'core_members', array( 'pp_reputation_points > 0' ), 'rep DESC', $limit )->setKeyField('member')->setValueField('rep') );
$topContributors = iterator_to_array( Db::i()->select( 'member_id as member, pp_reputation_points as rep', 'core_members', array( 'pp_reputation_points > 0' ), 'rep DESC', $limit )->setKeyField('member')->setValueField('rep') );
}
/* Contruct their data */
/* The PopularContributorsType will call ::load on the member ID to get the object */
foreach ( \IPS\Db::i()->select( '*', 'core_members', \IPS\Db::i()->in( 'member_id', array_keys( $topContributors ) ) ) as $member )
foreach ( Db::i()->select( '*', 'core_members', Db::i()->in( 'member_id', array_keys( $topContributors ) ) ) as $member )
{
\IPS\Member::constructFromData( $member );
Member::constructFromData( $member );
}
$output = array();
@@ -10,25 +10,34 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application\Module;
use IPS\Content\Search\ContentFilter;
use IPS\Content\Search\Query;
use IPS\core\api\GraphQL\Types\SearchType;
use IPS\core\modules\front\search\search as searchController;
use IPS\Db;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Search query for GraphQL API
*/
class _Search
class Search
{
/*
* @brief Query description
*/
public static $description = "Returns search results";
public static string $description = "Returns search results";
/*
* Query arguments
@@ -41,7 +50,7 @@ class _Search
'type' => TypeRegistry::eNum([
'name' => 'core_search_types_input',
'description' => "The available search types",
'values' => array_merge( array('core_members'), array_keys( \IPS\core\modules\front\search\search::contentTypes() ) )
'values' => array_merge( array('core_members'), array_keys( searchController::contentTypes() ) )
]),
],
'offset' => [
@@ -73,7 +82,7 @@ class _Search
/**
* Return the query return type
*/
public function type()
public function type() : SearchType
{
return \IPS\core\api\GraphQL\TypeRegistry::search();
}
@@ -81,12 +90,12 @@ class _Search
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return array
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : array
{
$where = array();
$returnObject = array();
@@ -94,7 +103,7 @@ class _Search
$limit = min( $args['limit'], 50 );
// Member search
if ( isset( $args['type'] ) and $args['type'] === 'core_members' and \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members', 'front' ) ) )
if ( isset( $args['type'] ) and $args['type'] === 'core_members' and Member::loggedIn()->canAccessModule( Module::get( 'core', 'members', 'front' ) ) )
{
if ( $args['term'] )
{
@@ -106,10 +115,10 @@ class _Search
}
// Ordering
$orderDir = isset( $args['orderDir'] ) ? $args['orderDir'] : 'DESC';
$orderDir = $args['orderDir'] ?? 'DESC';
$orderBy = 'name';
if( isset( $args['orderBy'] ) && \in_array( $args['orderBy'], array( 'joined', 'name', 'member_posts', 'pp_reputation_points' ) ) )
if( isset( $args['orderBy'] ) && in_array( $args['orderBy'], array( 'joined', 'name', 'member_posts', 'pp_reputation_points' ) ) )
{
$orderBy = $args['orderBy'];
}
@@ -117,26 +126,26 @@ class _Search
$order = $orderBy . ' ' . $orderDir;
// Get results
$select = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where );
$select = Db::i()->select( 'COUNT(*)', 'core_members', $where );
$select->join( 'core_pfields_content', 'core_pfields_content.member_id=core_members.member_id' );
$returnObject['count'] = $select->first();
$select = \IPS\Db::i()->select( 'core_members.*', 'core_members', $where, $order, array( $offset, $limit ) );
$select = Db::i()->select( 'core_members.*', 'core_members', $where, $order, array( $offset, $limit ) );
$select->join( 'core_pfields_content', 'core_pfields_content.member_id=core_members.member_id' );
$returnObject['results'] = new \IPS\Patterns\ActiveRecordIterator( $select, 'IPS\Member' );
$returnObject['results'] = new ActiveRecordIterator( $select, 'IPS\Member' );
return $returnObject;
}
// Content search
$query = \IPS\Content\Search\Query::init();
$types = \IPS\core\modules\front\search\search::contentTypes();
$query = Query::init();
$types = searchController::contentTypes();
if( isset( $args['type'] ) && !empty( $args['type'] ) )
if( !empty( $args['type'] ) )
{
$class = $types[ $args['type'] ];
$filter = \IPS\Content\Search\ContentFilter::init( $class );
$filter = ContentFilter::init( $class );
$query->filterByContent( array( $filter ) );
}
@@ -148,10 +157,10 @@ class _Search
switch( $args['orderBy'] )
{
case 'newest':
$query->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_CREATED );
$query->setOrder( Query::ORDER_NEWEST_CREATED );
break;
case 'relevancy':
$query->setOrder( \IPS\Content\Search\Query::ORDER_RELEVANCY );
$query->setOrder( Query::ORDER_RELEVANCY );
break;
default:
$query->setOrder( $orderBy );
@@ -179,7 +188,7 @@ class _Search
$returnObject['results'] = $query->search(
isset( $args['term'] ) ? ( $args['term'] ) : NULL,
NULL,
\IPS\Content\Search\Query::TERM_AND_TAGS + \IPS\Content\Search\Query::TAGS_MATCH_ITEMS_ONLY,
Query::TERM_AND_TAGS + Query::TAGS_MATCH_ITEMS_ONLY,
NULL
);
$returnObject['count'] = $returnObject['results']->count( TRUE );
@@ -10,26 +10,28 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\SettingsType;
use IPS\Settings as SettingsClass;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Settings query for GraphQL API
*/
class _Settings
class Settings
{
/*
* @brief Query description
*/
public static $description = "Returns values of system settings (subject to the setting being exposed by the API)";
public static string $description = "Returns values of system settings (subject to the setting being exposed by the API)";
/*
* Query arguments
@@ -42,21 +44,21 @@ class _Settings
/**
* Return the query return type
*/
public function type()
public function type() : SettingsType
{
return \IPS\core\api\GraphQL\TypeRegistry::settings();
return TypeRegistry::settings();
}
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\Settings
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return SettingsClass
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : SettingsClass
{
return \IPS\Settings::i();
return SettingsClass::i();
}
}
+14 -13
View File
@@ -10,25 +10,26 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\StatsType;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Stats query for GraphQL API
*/
class _Stats
class Stats
{
/*
* @brief Query description
*/
public static $description = "Returns community stats";
public static string $description = "Returns community stats";
/*
* Query arguments
@@ -41,20 +42,20 @@ class _Stats
/**
* Return the query return type
*/
public function type()
public function type() : StatsType
{
return \IPS\core\api\GraphQL\TypeRegistry::stats();
return TypeRegistry::stats();
}
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return mixed
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : mixed
{
return $val;
}
@@ -10,25 +10,31 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\StreamType;
use IPS\core\Stream as StreamClass;
use IPS\Member;
use OutOfRangeException;
use function defined;
use function intval;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Stream query for GraphQL API
*/
class _Stream
class Stream
{
/*
* @brief Query description
*/
public static $description = "Returns an activity stream";
public static string $description = "Returns an activity stream";
/*
* Query arguments
@@ -43,7 +49,7 @@ class _Stream
/**
* Return the query return type
*/
public function type()
public function type() : StreamType
{
return \IPS\core\api\GraphQL\TypeRegistry::stream();
}
@@ -51,34 +57,34 @@ class _Stream
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @return \IPS\core\Stream
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return StreamClass|null
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : ?StreamClass
{
if( isset( $args['id'] ) && \intval( $args['id'] ) )
if( isset( $args['id'] ) && intval( $args['id'] ) )
{
try
{
$stream = \IPS\core\Stream::load( $args['id'] );
$stream = StreamClass::load( $args['id'] );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
return NULL;
}
/* Suitable for guests? */
if ( !\IPS\Member::loggedIn()->member_id and !( ( $stream->ownership == 'all' or $stream->ownership == 'custom' ) and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) )
if ( !Member::loggedIn()->member_id and !( ( $stream->ownership == 'all' or $stream->ownership == 'custom' ) and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_STREAM', 'GQL/0003/1', 403 );
throw new SafeException( 'INVALID_STREAM', 'GQL/0003/1', 403 );
}
}
else
{
/* Start with a blank stream */
$stream = \IPS\core\Stream::allActivityStream();
$stream = StreamClass::allActivityStream();
}
return $stream;
@@ -10,26 +10,31 @@
*/
namespace IPS\core\api\GraphQL\Queries;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ListOfType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Types\StreamType;
use IPS\Db;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Streams query for GraphQL API
*/
class _Streams
class Streams
{
/*
* @brief Query description
*/
public static $description = "Returns a list of user's activity streams";
public static string $description = "Returns a list of user's activity streams";
/*
* @brief Query arguments
@@ -41,8 +46,10 @@ class _Streams
/**
* Return the query return type
*
* @return ListOfType<StreamType>
*/
public function type()
public function type() : ListOfType
{
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::stream() );
}
@@ -50,26 +57,26 @@ class _Streams
/**
* Resolves this query
*
* @param mixed Value passed into this resolver
* @param array Arguments
* @param array Context values
* @param mixed $val Value passed into this resolver
* @param array $args Arguments
* @param array $context Context values
* @return array
*/
public function resolve($val, $args, $context)
public function resolve( mixed $val, array $args, array $context ) : array
{
$streams = array();
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_streams', '`member` IS NULL', 'position ASC' ), 'IPS\core\Stream' ) as $stream )
foreach ( new ActiveRecordIterator( Db::i()->select( '*', 'core_streams', '`member` IS NULL', 'position ASC' ), 'IPS\core\Stream' ) as $stream )
{
if( \IPS\Member::loggedIn()->member_id || ( !\IPS\Member::loggedIn()->member_id && ( $stream->ownership == 'all' and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) ) )
if( Member::loggedIn()->member_id || ( !Member::loggedIn()->member_id && ( $stream->ownership == 'all' and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) ) )
{
$streams[ $stream->id ] = $stream;
}
}
if ( \IPS\Member::loggedIn()->member_id )
if ( Member::loggedIn()->member_id )
{
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_streams', array( '`member`=?', \IPS\Member::loggedIn()->member_id ), 'position ASC' ), 'IPS\core\Stream' ) as $stream )
foreach ( new ActiveRecordIterator( Db::i()->select( '*', 'core_streams', array( '`member`=?', Member::loggedIn()->member_id ), 'position ASC' ), 'IPS\core\Stream' ) as $stream )
{
$streams[ $stream->id ] = $stream;
}
+46 -26
View File
@@ -10,13 +10,33 @@
*/
namespace IPS\core\api\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\Queries\ActiveUsers;
use IPS\core\api\GraphQL\Queries\Club;
use IPS\core\api\GraphQL\Queries\Clubs;
use IPS\core\api\GraphQL\Queries\Content;
use IPS\core\api\GraphQL\Queries\Group;
use IPS\core\api\GraphQL\Queries\Language;
use IPS\core\api\GraphQL\Queries\LoginHandlers;
use IPS\core\api\GraphQL\Queries\Me;
use IPS\core\api\GraphQL\Queries\Member;
use IPS\core\api\GraphQL\Queries\Members;
use IPS\core\api\GraphQL\Queries\MessengerConversation;
use IPS\core\api\GraphQL\Queries\MessengerConversations;
use IPS\core\api\GraphQL\Queries\MessengerFolders;
use IPS\core\api\GraphQL\Queries\NotificationTypes;
use IPS\core\api\GraphQL\Queries\OurPicks;
use IPS\core\api\GraphQL\Queries\PopularContributors;
use IPS\core\api\GraphQL\Queries\Search;
use IPS\core\api\GraphQL\Queries\Settings;
use IPS\core\api\GraphQL\Queries\Stats;
use IPS\core\api\GraphQL\Queries\Stream;
use IPS\core\api\GraphQL\Queries\Streams;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
@@ -24,7 +44,7 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Core controller for GraphQL API
* @todo maybe this shouldn't be a class since it only has a static method?
*/
abstract class _Query
abstract class Query
{
/**
@@ -35,27 +55,27 @@ abstract class _Query
public static function queries(): array
{
return [
'activeUsers' => new \IPS\core\api\GraphQL\Queries\ActiveUsers(),
'club' => new \IPS\core\api\GraphQL\Queries\Club(),
'clubs' => new \IPS\core\api\GraphQL\Queries\Clubs(),
'content' => new \IPS\core\api\GraphQL\Queries\Content(),
'group' => new \IPS\core\api\GraphQL\Queries\Group(),
'language' => new \IPS\core\api\GraphQL\Queries\Language(),
'loginHandlers' => new \IPS\core\api\GraphQL\Queries\LoginHandlers(),
'me' => new \IPS\core\api\GraphQL\Queries\Me(),
'member' => new \IPS\core\api\GraphQL\Queries\Member(),
'members' => new \IPS\core\api\GraphQL\Queries\Members(),
'messengerConversation' => new \IPS\core\api\GraphQL\Queries\MessengerConversation(),
'messengerConversations' => new \IPS\core\api\GraphQL\Queries\MessengerConversations(),
'messengerFolders' => new \IPS\core\api\GraphQL\Queries\MessengerFolders(),
'notificationTypes' => new \IPS\core\api\GraphQL\Queries\NotificationTypes(),
'ourPicks' => new \IPS\core\api\GraphQL\Queries\OurPicks(),
'popularContributors' => new \IPS\core\api\GraphQL\Queries\PopularContributors(),
'search' => new \IPS\core\api\GraphQL\Queries\Search(),
'settings' => new \IPS\core\api\GraphQL\Queries\Settings(),
'stats' => new \IPS\core\api\GraphQL\Queries\Stats(),
'stream' => new \IPS\core\api\GraphQL\Queries\Stream(),
'streams' => new \IPS\core\api\GraphQL\Queries\Streams(),
'activeUsers' => new ActiveUsers(),
'club' => new Club(),
'clubs' => new Clubs(),
'content' => new Content(),
'group' => new Group(),
'language' => new Language(),
'loginHandlers' => new LoginHandlers(),
'me' => new Me(),
'member' => new Member(),
'members' => new Members(),
'messengerConversation' => new MessengerConversation(),
'messengerConversations' => new MessengerConversations(),
'messengerFolders' => new MessengerFolders(),
'notificationTypes' => new NotificationTypes(),
'ourPicks' => new OurPicks(),
'popularContributors' => new PopularContributors(),
'search' => new Search(),
'settings' => new Settings(),
'stats' => new Stats(),
'stream' => new Stream(),
'streams' => new Streams(),
];
}
}
+179 -145
View File
@@ -10,13 +10,47 @@
*/
namespace IPS\core\api\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\Types;
use IPS\core\api\GraphQL\Types\ActiveUsersType;
use IPS\core\api\GraphQL\Types\ActiveUserType;
use IPS\core\api\GraphQL\Types\AttachmentPermissionsType;
use IPS\core\api\GraphQL\Types\AttachmentType;
use IPS\core\api\GraphQL\Types\ClubNodeType;
use IPS\core\api\GraphQL\Types\ClubType;
use IPS\core\api\GraphQL\Types\ContentReactionType;
use IPS\core\api\GraphQL\Types\ContentSearchResultType;
use IPS\core\api\GraphQL\Types\GroupType;
use IPS\core\api\GraphQL\Types\IgnoreOptionType;
use IPS\core\api\GraphQL\Types\LanguageType;
use IPS\core\api\GraphQL\Types\LoginType;
use IPS\core\api\GraphQL\Types\MemberType;
use IPS\core\api\GraphQL\Types\MessengerConversationType;
use IPS\core\api\GraphQL\Types\MessengerFolderType;
use IPS\core\api\GraphQL\Types\MessengerParticipantType;
use IPS\core\api\GraphQL\Types\MessengerReplyType;
use IPS\core\api\GraphQL\Types\NotificationMethodType;
use IPS\core\api\GraphQL\Types\NotificationType;
use IPS\core\api\GraphQL\Types\NotificationTypeType;
use IPS\core\api\GraphQL\Types\PollQuestionType;
use IPS\core\api\GraphQL\Types\PollType;
use IPS\core\api\GraphQL\Types\PopularContributorType;
use IPS\core\api\GraphQL\Types\ProfileFieldGroupType;
use IPS\core\api\GraphQL\Types\ProfileFieldType;
use IPS\core\api\GraphQL\Types\PromotedItemType;
use IPS\core\api\GraphQL\Types\ReportReasonType;
use IPS\core\api\GraphQL\Types\ReportType;
use IPS\core\api\GraphQL\Types\SearchResultType;
use IPS\core\api\GraphQL\Types\SearchType;
use IPS\core\api\GraphQL\Types\SettingsType;
use IPS\core\api\GraphQL\Types\StatsType;
use IPS\core\api\GraphQL\Types\StreamType;
use IPS\core\api\GraphQL\Types\TagType;
use IPS\core\api\GraphQL\Types\UploadProgressType;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
@@ -25,43 +59,43 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* so we'll generate singletons here.
* @todo automate this somehow?
*/
class _TypeRegistry
class TypeRegistry
{
protected static $activeUsers;
protected static $activeUser;
protected static $attachment;
protected static $attachmentPermissions;
protected static $clubNode;
protected static $club;
protected static $contentReaction;
protected static $contentSearchResult;
protected static $group;
protected static $ignoreOption;
protected static $member;
protected static $language;
protected static $login;
protected static $messengerConversation;
protected static $messengerFolder;
protected static $messengerParticipant;
protected static $messengerReply;
protected static $notification;
protected static $notificationMethod;
protected static $notificationType;
protected static $poll;
protected static $pollQuestion;
protected static $popularContributor;
protected static $profileFieldGroup;
protected static $profileField;
protected static $promotedItem;
protected static $report;
protected static $reportReason;
protected static $search;
protected static $searchResult;
protected static $stats;
protected static $stream;
protected static $tag;
protected static $settings;
protected static $uploadProgress;
protected static ?ActiveUsersType $activeUsers = null;
protected static ?ActiveUserType $activeUser = null;
protected static ?AttachmentType $attachment = null;
protected static ?AttachmentPermissionsType $attachmentPermissions = null;
protected static ?ClubNodeType $clubNode = null;
protected static ?ClubType $club = null;
protected static ?ContentReactionType $contentReaction = null;
protected static ?ContentSearchResultType $contentSearchResult = null;
protected static ?GroupType $group = null;
protected static ?IgnoreOptionType $ignoreOption = null;
protected static ?MemberType $member = null;
protected static ?LanguageType $language = null;
protected static ?LoginType $login = null;
protected static ?MessengerConversationType $messengerConversation = null;
protected static ?MessengerFolderType $messengerFolder = null;
protected static ?MessengerParticipantType $messengerParticipant = null;
protected static ?MessengerReplyType $messengerReply = null;
protected static ?NotificationType $notification = null;
protected static ?NotificationMethodType $notificationMethod = null;
protected static ?NotificationTypeType $notificationType = null;
protected static ?PollType $poll = null;
protected static ?PollQuestionType $pollQuestion = null;
protected static ?PopularContributorType $popularContributor = null;
protected static ?ProfileFieldGroupType $profileFieldGroup = null;
protected static ?ProfileFieldType $profileField = null;
protected static ?PromotedItemType $promotedItem = null;
protected static ?ReportType $report = null;
protected static ?ReportReasonType $reportReason = null;
protected static ?SearchType $search = null;
protected static ?SearchResultType $searchResult = null;
protected static ?StatsType $stats = null;
protected static ?StreamType $stream = null;
protected static ?TagType $tag = null;
protected static ?SettingsType $settings = null;
protected static ?UploadProgressType $uploadProgress = null;
/**
* Constructor
@@ -72,282 +106,282 @@ class _TypeRegistry
}
/**
* @return \IPS\core\api\GraphQL\Types\ActiveUsersType
* @return ActiveUsersType
*/
public static function activeUsers()
public static function activeUsers() : ActiveUsersType
{
return self::$activeUsers ?: (self::$activeUsers = new \IPS\core\api\GraphQL\Types\ActiveUsersType());
return self::$activeUsers ?: (self::$activeUsers = new ActiveUsersType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ActiveUserType
* @return ActiveUserType
*/
public static function activeUser()
public static function activeUser() : ActiveUserType
{
return self::$activeUser ?: (self::$activeUser = new \IPS\core\api\GraphQL\Types\ActiveUserType());
return self::$activeUser ?: (self::$activeUser = new ActiveUserType());
}
/**
* @return \IPS\core\api\GraphQL\Types\AttachmentType
* @return AttachmentType
*/
public static function attachment()
public static function attachment() : AttachmentType
{
return self::$attachment ?: (self::$attachment = new \IPS\core\api\GraphQL\Types\AttachmentType());
return self::$attachment ?: (self::$attachment = new AttachmentType());
}
/**
* @return \IPS\core\api\GraphQL\Types\AttachmentPermissionsType
* @return AttachmentPermissionsType
*/
public static function attachmentPermissions()
public static function attachmentPermissions() : AttachmentPermissionsType
{
return self::$attachmentPermissions ?: (self::$attachmentPermissions = new \IPS\core\api\GraphQL\Types\AttachmentPermissionsType());
return self::$attachmentPermissions ?: (self::$attachmentPermissions = new AttachmentPermissionsType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ClubNodeType
* @return ClubNodeType
*/
public static function clubNode()
public static function clubNode() : ClubNodeType
{
return self::$clubNode ?: (self::$clubNode = new \IPS\core\api\GraphQL\Types\ClubNodeType());
return self::$clubNode ?: (self::$clubNode = new ClubNodeType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ClubType
* @return ClubType
*/
public static function club()
public static function club() : ClubType
{
return self::$club ?: (self::$club = new \IPS\core\api\GraphQL\Types\ClubType());
return self::$club ?: (self::$club = new ClubType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ContentReactionType
* @return ContentReactionType
*/
public static function contentReaction()
public static function contentReaction() : ContentReactionType
{
return self::$contentReaction ?: (self::$contentReaction = new \IPS\core\api\GraphQL\Types\ContentReactionType());
return self::$contentReaction ?: (self::$contentReaction = new ContentReactionType());
}
/**
* @return \IPS\core\api\GraphQL\Types\GroupType
* @return GroupType
*/
public static function group()
public static function group() : GroupType
{
return self::$group ?: (self::$group = new \IPS\core\api\GraphQL\Types\GroupType());
return self::$group ?: (self::$group = new GroupType());
}
/**
* @return \IPS\core\api\GraphQL\Types\IgnoreOptionType
* @return IgnoreOptionType
*/
public static function ignoreOption()
public static function ignoreOption() : IgnoreOptionType
{
return self::$ignoreOption ?: (self::$ignoreOption = new \IPS\core\api\GraphQL\Types\IgnoreOptionType());
return self::$ignoreOption ?: (self::$ignoreOption = new IgnoreOptionType());
}
/**
* @return \IPS\core\api\GraphQL\Types\MemberType
* @return MemberType
*/
public static function member()
public static function member() : MemberType
{
return self::$member ?: (self::$member = new \IPS\core\api\GraphQL\Types\MemberType());
return self::$member ?: (self::$member = new MemberType());
}
/**
* @return \IPS\core\api\GraphQL\Types\LanguageType
* @return LanguageType
*/
public static function language()
public static function language() : LanguageType
{
return self::$language ?: (self::$language = new \IPS\core\api\GraphQL\Types\LanguageType());
return self::$language ?: (self::$language = new LanguageType());
}
/**
* @return \IPS\core\api\GraphQL\Types\LoginType
* @return LoginType
*/
public static function login()
public static function login() : LoginType
{
return self::$login ?: (self::$login = new \IPS\core\api\GraphQL\Types\LoginType());
return self::$login ?: (self::$login = new LoginType());
}
/**
* @return \IPS\core\api\GraphQL\Types\MessengerConversationType
* @return MessengerConversationType
*/
public static function messengerConversation()
public static function messengerConversation() : MessengerConversationType
{
return self::$messengerConversation ?: (self::$messengerConversation = new \IPS\core\api\GraphQL\Types\MessengerConversationType());
return self::$messengerConversation ?: (self::$messengerConversation = new MessengerConversationType());
}
/**
* @return \IPS\core\api\GraphQL\Types\MessengerFolderType
* @return MessengerFolderType
*/
public static function messengerFolder()
public static function messengerFolder() : MessengerFolderType
{
return self::$messengerFolder ?: (self::$messengerFolder = new \IPS\core\api\GraphQL\Types\MessengerFolderType());
return self::$messengerFolder ?: (self::$messengerFolder = new MessengerFolderType());
}
/**
* @return \IPS\core\api\GraphQL\Types\MessengerParticipantType
* @return MessengerParticipantType
*/
public static function messengerParticipant()
public static function messengerParticipant() : MessengerParticipantType
{
return self::$messengerParticipant ?: (self::$messengerParticipant = new \IPS\core\api\GraphQL\Types\MessengerParticipantType());
return self::$messengerParticipant ?: (self::$messengerParticipant = new MessengerParticipantType());
}
/**
* @return \IPS\core\api\GraphQL\Types\MessengerReplyType
* @return MessengerReplyType
*/
public static function messengerReply()
public static function messengerReply() : MessengerReplyType
{
return self::$messengerReply ?: (self::$messengerReply = new \IPS\core\api\GraphQL\Types\MessengerReplyType());
return self::$messengerReply ?: (self::$messengerReply = new MessengerReplyType());
}
/**
* @return \IPS\core\api\GraphQL\Types\NotificationType
* @return NotificationType
*/
public static function notification()
public static function notification() : NotificationType
{
return self::$notification ?: (self::$notification = new \IPS\core\api\GraphQL\Types\NotificationType());
return self::$notification ?: (self::$notification = new NotificationType());
}
/**
* @return \IPS\core\api\GraphQL\Types\NotificationMethodType
* @return NotificationMethodType
*/
public static function notificationMethod()
public static function notificationMethod() : NotificationMethodType
{
return self::$notificationMethod ?: (self::$notificationMethod = new \IPS\core\api\GraphQL\Types\NotificationMethodType());
return self::$notificationMethod ?: (self::$notificationMethod = new NotificationMethodType());
}
/**
* @return \IPS\core\api\GraphQL\Types\NotificationTypeType
* @return NotificationTypeType
*/
public static function notificationType()
public static function notificationType() : NotificationTypeType
{
return self::$notificationType ?: (self::$notificationType = new \IPS\core\api\GraphQL\Types\NotificationTypeType());
return self::$notificationType ?: (self::$notificationType = new NotificationTypeType());
}
/**
* @return \IPS\core\api\GraphQL\Types\PollType
* @return PollType
*/
public static function poll()
public static function poll() : PollType
{
return self::$poll ?: (self::$poll = new \IPS\core\api\GraphQL\Types\PollType());
return self::$poll ?: (self::$poll = new PollType());
}
/**
* @return \IPS\core\api\GraphQL\Types\PollQuestionType
* @return PollQuestionType
*/
public static function pollQuestion(): \IPS\core\api\GraphQL\Types\PollQuestionType
public static function pollQuestion(): PollQuestionType
{
return self::$pollQuestion ?: (self::$pollQuestion = new \IPS\core\api\GraphQL\Types\PollQuestionType());
return self::$pollQuestion ?: (self::$pollQuestion = new PollQuestionType());
}
/**
* @return \IPS\core\api\GraphQL\Types\PopularContributorType
* @return PopularContributorType
*/
public static function popularContributor(): \IPS\core\api\GraphQL\Types\PopularContributorType
public static function popularContributor(): PopularContributorType
{
return self::$popularContributor ?: (self::$popularContributor = new \IPS\core\api\GraphQL\Types\PopularContributorType());
return self::$popularContributor ?: (self::$popularContributor = new PopularContributorType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ProfileFieldGroupType
* @return ProfileFieldGroupType
*/
public static function profileFieldGroup(): \IPS\core\api\GraphQL\Types\ProfileFieldGroupType
public static function profileFieldGroup(): ProfileFieldGroupType
{
return self::$profileFieldGroup ?: (self::$profileFieldGroup = new \IPS\core\api\GraphQL\Types\ProfileFieldGroupType());
return self::$profileFieldGroup ?: (self::$profileFieldGroup = new ProfileFieldGroupType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ProfileFieldType
* @return ProfileFieldType
*/
public static function profileField(): \IPS\core\api\GraphQL\Types\ProfileFieldType
public static function profileField(): ProfileFieldType
{
return self::$profileField ?: (self::$profileField = new \IPS\core\api\GraphQL\Types\ProfileFieldType());
return self::$profileField ?: (self::$profileField = new ProfileFieldType());
}
/**
* @return \IPS\core\api\GraphQL\Types\PromotedItemType
* @return PromotedItemType
*/
public static function promotedItem(): \IPS\core\api\GraphQL\Types\PromotedItemType
public static function promotedItem(): PromotedItemType
{
return self::$promotedItem ?: (self::$promotedItem = new \IPS\core\api\GraphQL\Types\PromotedItemType());
return self::$promotedItem ?: (self::$promotedItem = new PromotedItemType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ContentSearchResultType
* @return ContentSearchResultType
*/
public static function contentSearchResult(): \IPS\core\api\GraphQL\Types\ContentSearchResultType
public static function contentSearchResult(): ContentSearchResultType
{
return self::$contentSearchResult ?: (self::$contentSearchResult = new \IPS\core\api\GraphQL\Types\ContentSearchResultType());
return self::$contentSearchResult ?: (self::$contentSearchResult = new ContentSearchResultType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ReportType
* @return ReportType
*/
public static function report(): \IPS\core\api\GraphQL\Types\ReportType
public static function report(): ReportType
{
return self::$report ?: (self::$report = new \IPS\core\api\GraphQL\Types\ReportType());
return self::$report ?: (self::$report = new ReportType());
}
/**
* @return \IPS\core\api\GraphQL\Types\ReportReasonType
* @return ReportReasonType
*/
public static function reportReason(): \IPS\core\api\GraphQL\Types\ReportReasonType
public static function reportReason(): ReportReasonType
{
return self::$reportReason ?: (self::$reportReason = new \IPS\core\api\GraphQL\Types\ReportReasonType());
return self::$reportReason ?: (self::$reportReason = new ReportReasonType());
}
/**
* @return \IPS\core\api\GraphQL\Types\SearchResultType
* @return SearchResultType
*/
public static function searchResult(): \IPS\core\api\GraphQL\Types\SearchResultType
public static function searchResult(): SearchResultType
{
return self::$searchResult ?: (self::$searchResult = new \IPS\core\api\GraphQL\Types\SearchResultType());
return self::$searchResult ?: (self::$searchResult = new SearchResultType());
}
/**
* @return \IPS\core\api\GraphQL\Types\SearchType
* @return SearchType
*/
public static function search(): \IPS\core\api\GraphQL\Types\SearchType
public static function search(): SearchType
{
return self::$search ?: (self::$search = new \IPS\core\api\GraphQL\Types\SearchType());
return self::$search ?: (self::$search = new SearchType());
}
/**
* @return \IPS\core\api\GraphQL\Types\StatsType
* @return StatsType
*/
public static function stats(): \IPS\core\api\GraphQL\Types\StatsType
public static function stats(): StatsType
{
return self::$stats ?: (self::$stats = new \IPS\core\api\GraphQL\Types\StatsType());
return self::$stats ?: (self::$stats = new StatsType());
}
/**
* @return \IPS\core\api\GraphQL\Types\StreamType
* @return StreamType
*/
public static function stream(): \IPS\core\api\GraphQL\Types\StreamType
public static function stream(): StreamType
{
return self::$stream ?: (self::$stream = new \IPS\core\api\GraphQL\Types\StreamType());
return self::$stream ?: (self::$stream = new StreamType());
}
/**
* @return \IPS\core\api\GraphQL\Types\TagType
* @return TagType
*/
public static function tag(): \IPS\core\api\GraphQL\Types\TagType
public static function tag(): TagType
{
return self::$tag ?: (self::$tag = new \IPS\core\api\GraphQL\Types\TagType());
return self::$tag ?: (self::$tag = new TagType());
}
/**
* @return \IPS\core\api\GraphQL\Types\SettingsType
* @return SettingsType
*/
public static function settings(): \IPS\core\api\GraphQL\Types\SettingsType
public static function settings(): SettingsType
{
return self::$settings ?: (self::$settings = new \IPS\core\api\GraphQL\Types\SettingsType());
return self::$settings ?: (self::$settings = new SettingsType());
}
/**
* @return \IPS\core\api\GraphQL\Types\UploadProgressType
* @return UploadProgressType
*/
public static function uploadProgress(): \IPS\core\api\GraphQL\Types\UploadProgressType
public static function uploadProgress(): UploadProgressType
{
return self::$uploadProgress ?: (self::$uploadProgress = new \IPS\core\api\GraphQL\Types\UploadProgressType());
return self::$uploadProgress ?: (self::$uploadProgress = new UploadProgressType());
}
}
@@ -12,23 +12,27 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Http\Url;
use IPS\Member;
use IPS\Session\Front;
use UnderflowException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ActiveUsers for GraphQL API
*/
class _ActiveUserType extends ObjectType
class ActiveUserType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -38,8 +42,8 @@ class _ActiveUserType extends ObjectType
return [
'url' => [
'type' => TypeRegistry::url(),
'resolve' => function ($user, $args) {
return \IPS\Http\Url::createFromString( $user['location_url'] );
'resolve' => function ($user) {
return Url::createFromString( $user['location_url'] );
}
],
'lang' => [
@@ -56,14 +60,14 @@ class _ActiveUserType extends ObjectType
],
'anonymous' => [
'type' => TypeRegistry::boolean(),
'resolve' => function ($user, $args) {
return $user['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS;
'resolve' => function ($user) {
return $user['login_type'] == Front::LOGIN_TYPE_ANONYMOUS;
}
],
'ipAddress' => [
'type' => TypeRegistry::string(),
'resolve' => function ($user, $args) {
if( \IPS\Member::loggedIn()->modPermission( 'can_use_ip_tools' ) )
'resolve' => function ($user) {
if( Member::loggedIn()->modPermission( 'can_use_ip_tools' ) )
{
return $user['ip_address'];
}
@@ -73,19 +77,19 @@ class _ActiveUserType extends ObjectType
],
'timestamp' => [
'type' => TypeRegistry::int(),
'resolve' => function ($user, $args) {
'resolve' => function ($user) {
return $user['running_time'];
}
],
'user' => [
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'resolve' => function ($user, $args) {
'resolve' => function ($user) {
if( $user['member_id'] )
{
return \IPS\Member::load( $user['member_id'] );
return Member::load( $user['member_id'] );
}
return new \IPS\Member;
return new Member;
}
]
];
@@ -100,18 +104,18 @@ class _ActiveUserType extends ObjectType
* @todo this calls language()->get(), so we need to figure out we can get the keys in advance to load them
* @param array $user The active user row from session store
* @param boolean $action Use the 'action' version of the language string?
* @return String
* @return string|null
*/
protected static function getLocationLang($user, $action)
protected static function getLocationLang( array $user, bool $action ) : ?string
{
try
{
if( !$user['location_lang'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'app_user_browsing_community', FALSE, array('sprintf' => array( $user['member_name'] ) ) );
return Member::loggedIn()->language()->addToStack( 'app_user_browsing_community', FALSE, array('sprintf' => array( $user['member_name'] ) ) );
}
if ( $user['location_permissions'] === NULL or $user['location_permissions'] === '*' or \IPS\Member::loggedIn()->inGroup( explode( ',', $user['location_permissions'] ), TRUE ) )
if ( $user['location_permissions'] === NULL or $user['location_permissions'] === '*' or Member::loggedIn()->inGroup( explode( ',', $user['location_permissions'] ), TRUE ) )
{
$sprintf = array();
$data = json_decode( $user['location_data'], TRUE );
@@ -120,26 +124,24 @@ class _ActiveUserType extends ObjectType
{
foreach ( $data as $key => $parse )
{
$value = htmlspecialchars( $parse ? \IPS\Member::loggedIn()->language()->get( $key ) : $key, ENT_DISALLOWED, 'UTF-8', FALSE );
$value = htmlspecialchars( $parse ? Member::loggedIn()->language()->get( $key ) : $key, ENT_DISALLOWED, 'UTF-8', FALSE );
$sprintf[] = $value;
}
}
if( $action && \IPS\Member::loggedIn()->language()->checkKeyExists($user['location_lang'] . '_action') )
if( $action && Member::loggedIn()->language()->checkKeyExists($user['location_lang'] . '_action') )
{
// Add the member name to the sprintf array since the action strings use it
array_unshift( $sprintf, $user['member_name'] );
return \IPS\Member::loggedIn()->language()->addToStack( $user['location_lang'] . '_action', FALSE, array( 'sprintf' => $sprintf ) );
return Member::loggedIn()->language()->addToStack( $user['location_lang'] . '_action', FALSE, array( 'sprintf' => $sprintf ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( $user['location_lang'], FALSE, array( 'sprintf' => $sprintf ) );
return Member::loggedIn()->language()->addToStack( $user['location_lang'], FALSE, array( 'sprintf' => $sprintf ) );
}
return sprintf( $lang, $sprintf );
}
}
catch ( \UnderflowException $e ){
catch ( UnderflowException $e ){
}
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use IPS\Session\Store;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ActiveUsers for GraphQL API
*/
class _ActiveUsersType extends ObjectType
class ActiveUsersType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -45,14 +47,14 @@ class _ActiveUsersType extends ObjectType
]
],
'resolve' => function ($val, $args) {
$flags = \IPS\Session\Store::ONLINE_MEMBERS;
$flags = Store::ONLINE_MEMBERS;
if( $args['includeGuests'] )
{
$flags = \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_GUESTS;
$flags = Store::ONLINE_MEMBERS | Store::ONLINE_GUESTS;
}
return \IPS\Session\Store::i()->getOnlineUsers( $flags | \IPS\Session\Store::ONLINE_COUNT_ONLY, 'asc', NULL, NULL, \IPS\Member::loggedIn()->isAdmin() );
return Store::i()->getOnlineUsers( $flags | Store::ONLINE_COUNT_ONLY, 'asc', NULL, NULL, Member::loggedIn()->isAdmin() );
}
],
'users' => [
@@ -79,17 +81,15 @@ class _ActiveUsersType extends ObjectType
]
],
'resolve' => function ($val, $args) {
$flags = \IPS\Session\Store::ONLINE_MEMBERS;
$flags = Store::ONLINE_MEMBERS;
if( $args['includeGuests'] )
{
$flags = \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_GUESTS;
$flags = Store::ONLINE_MEMBERS | Store::ONLINE_GUESTS;
}
$offset = max( $args['offset'], 0 );
$limit = min( $args['limit'], 50 );
$users = \IPS\Session\Store::i()->getOnlineUsers( $flags, $args['sortDir'], array( $offset, $limit ), NULL, \IPS\Member::loggedIn()->isAdmin() );
return $users;
return Store::i()->getOnlineUsers( $flags, $args['sortDir'], array( $offset, $limit ), NULL, Member::loggedIn()->isAdmin() );
}
]
];
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Db;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* AttachmentPermissionsType for GraphQL API
*/
class _AttachmentPermissionsType extends ObjectType
class AttachmentPermissionsType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -43,7 +44,7 @@ class _AttachmentPermissionsType extends ObjectType
'resolve' => function( $data ) {
$currentPostUsage = 0;
// @todo Currently this only considers postKey because the GraphQL doesn't support editing. When edit support is added we'll need to modify this query to do "OR ( location_key=? AND id=? AND id2=?... )"
foreach ( \IPS\Db::i()->select( '*', 'core_attachments', array( 'attach_post_key=?', $data['postKey'] ) ) as $attachment )
foreach ( Db::i()->select( '*', 'core_attachments', array( 'attach_post_key=?', $data['postKey'] ) ) as $attachment )
{
$currentPostUsage += $attachment['attach_filesize'];
}
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\File;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* AttachmentType for GraphQL API
*/
class _AttachmentType extends ObjectType
class AttachmentType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -72,7 +74,7 @@ class _AttachmentType extends ObjectType
if ( $attachment['attach_is_image'] )
{
return array(
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->url,
'url' => (string) File::get( 'core_Attachment', $attachment['attach_location'] )->url,
'width' => $attachment['attach_img_width'],
'height' => $attachment['attach_img_height'],
);
@@ -89,7 +91,7 @@ class _AttachmentType extends ObjectType
if ( $attachment['attach_thumb_location'] )
{
return array(
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->url,
'url' => (string) File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->url,
'width' => $attachment['attach_thumb_width'],
'height' => $attachment['attach_thumb_height'],
);
@@ -97,7 +99,7 @@ class _AttachmentType extends ObjectType
else
{
return array(
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->url,
'url' => (string) File::get( 'core_Attachment', $attachment['attach_location'] )->url,
'width' => $attachment['attach_img_width'],
'height' => $attachment['attach_img_height'],
);
@@ -117,7 +119,7 @@ class _AttachmentType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'description' => "Member who uploaded the attachment",
'resolve' => function ( $attachment ) {
return \IPS\Member::load( $attachment['attach_member_id'] );
return Member::load( $attachment['attach_member_id'] );
}
],
];
@@ -11,25 +11,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\UnionType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Http\Url\Friendly;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ClubNodeType for GraphQL API
*/
class _ClubNodeType extends ObjectType
class ClubNodeType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -41,28 +41,28 @@ class _ClubNodeType extends ObjectType
'id' => [
'type' => TypeRegistry::id(),
'description' => "Node ID",
'resolve' => function ($node, $args, $context, $info) {
'resolve' => function ($node) {
return $node['node_id'];
}
],
'name' => [
'type' => TypeRegistry::string(),
'description' => "Node name",
'resolve' => function ($node, $args, $context, $info) {
'resolve' => function ($node) {
return $node['name'];
}
],
'seoTitle' => [
'type' => TypeRegistry::string(),
'description' => "Node's SEO title",
'resolve' => function ($node, $args, $context, $info) {
return \IPS\Http\Url\Friendly::seoTitle( $node['name'] );
'resolve' => function ($node) {
return Friendly::seoTitle( $node['name'] );
}
],
'type' => [
'type' => TypeRegistry::string(),
'description' => "Node type (from classname)",
'resolve' => function ($node, $args, $context, $info) {
'resolve' => function ($node) {
return str_replace('\\', '_', str_replace('IPS\\', '', $node['node_class'] ) );
}
]
@@ -12,23 +12,27 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\File;
use IPS\Http\Url\Friendly;
use IPS\Member;
use IPS\Member\Club;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ClubType for GraphQL API
*/
class _ClubType extends ObjectType
class ClubType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -62,7 +66,7 @@ class _ClubType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Club SEO title",
'resolve' => function ($club) {
return \IPS\Http\Url\Friendly::seoTitle( $club->name );
return Friendly::seoTitle( $club->name );
}
],
'createdDate' => [
@@ -110,7 +114,11 @@ class _ClubType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Club icon image",
'resolve' => function ($club) {
return ( $club->profile_photo ) ? \IPS\File::get( 'core_Clubs', $club->profile_photo )->url : null;
if( $club->profile_photo )
{
return File::get( 'core_Clubs', $club->profile_photo )->url;
}
return null;
}
],
'coverPhoto' => [
@@ -158,11 +166,11 @@ class _ClubType extends ObjectType
/**
* Resolve nodes field
*
* @param \IPS\Member\Club
* @param array Arguments passed to this resolver
* @param Club $club
* @param array $args Arguments passed to this resolver
* @return array
*/
protected static function nodes($club, $args)
protected static function nodes( Club $club, array $args ) : array
{
return $club->nodes();
}
@@ -170,11 +178,11 @@ class _ClubType extends ObjectType
/**
* Resolve members field
*
* @param \IPS\Member\Club
* @param array Arguments passed to this resolver
* @param Club $club
* @param array $args Arguments passed to this resolver
* @return array
*/
protected static function members($club, $args)
protected static function members( Club $club, array $args ) : array
{
$memberType = ( $args['type'] == 'all' ) ? array( 'member', 'moderator', 'leader' ) : array( $args['type'] );
$result = array();
@@ -182,7 +190,7 @@ class _ClubType extends ObjectType
$members = $club->members( $memberType, $limit, 'core_clubs_memberships.joined ASC', 2 );
foreach( $members as $memberRow ){
$result[] = \IPS\Member::constructFromData( $memberRow );
$result[] = Member::constructFromData( $memberRow );
}
return $result;
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Reaction;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ContentReactionType for GraphQL API
*/
class _ContentReactionType extends ObjectType
class ContentReactionType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -55,14 +57,14 @@ class _ContentReactionType extends ObjectType
'type' => TypeRegistry::int(),
'description' => "Count of this reaction type",
'resolve' => function ($reaction) {
return isset( $reaction['count'] ) ? $reaction['count'] : 0;
return $reaction['count'] ?? 0;
}
],
'image' => [
'type' => TypeRegistry::string(),
'description' => "Reaction image",
'resolve' => function ($reaction) {
$_reaction = isset( $reaction['reaction'] ) ? $reaction['reaction'] : \IPS\Content\Reaction::load( $reaction['reactionId'] );
$_reaction = $reaction['reaction'] ?? Reaction::load( $reaction['reactionId'] );
return (string) $_reaction->_icon->url;
}
],
@@ -70,8 +72,8 @@ class _ContentReactionType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Reaction name",
'resolve' => function ($reaction) {
$_reaction = isset( $reaction['reaction'] ) ? $reaction['reaction'] : \IPS\Content\Reaction::load( $reaction['reactionId'] );
return \IPS\Member::loggedIn()->language()->addToStack("reaction_title_{$_reaction->id}", FALSE, array('escape' => TRUE));
$_reaction = $reaction['reaction'] ?? Reaction::load( $reaction['reactionId'] );
return Member::loggedIn()->language()->addToStack("reaction_title_{$_reaction->id}", FALSE, array('escape' => TRUE));
}
]
];
@@ -10,25 +10,36 @@
*/
namespace IPS\core\api\GraphQL\Types;
use Exception;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Comment;
use IPS\Content\Item;
use IPS\Content\Reaction;
use IPS\Content\Search\Result;
use IPS\File;
use IPS\Http\Url;
use IPS\Member;
use IPS\Member\Club;
use function count;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ContentSearchResultType for GraphQL API
*/
class _ContentSearchResultType extends ObjectType
class ContentSearchResultType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -91,9 +102,7 @@ class _ContentSearchResultType extends ObjectType
'description' => "Class of the item type, if this result is a comment/review",
'resolve' => function ($result) {
$asArray = $result->asArray();
$itemClass = static::getItemClass( $asArray );
return $itemClass;
return static::getItemClass( $asArray );
}
],
// @todo this should really be in a more generic ClassType that returns
@@ -103,6 +112,8 @@ class _ContentSearchResultType extends ObjectType
'description' => "Does the result class require a first comment (e.g. topics)?",
'resolve' => function ($result) {
$asArray = $result->asArray();
/* @var Item $itemClass */
$itemClass = static::getItemClass( $asArray );
return $itemClass::$firstCommentRequired;
@@ -148,20 +159,23 @@ class _ContentSearchResultType extends ObjectType
],
'resolveField' => function ($result, $args, $context, $info) {
$asArray = $result->asArray();
/* @var Item $classToUse */
$classToUse = static::getItemClass( $asArray );
switch( $info->fieldName )
{
case 'indefinite':
return $classToUse::_indefiniteArticle( $asArray['containerData'] );
break;
case 'definite':
return $classToUse::_definiteArticle( $asArray['containerData'] );
break;
case 'definiteUC':
return $classToUse::_definiteArticle( $asArray['containerData'], NULL, array( 'ucfirst' => TRUE ) );
break;
}
return '';
}
]),
'resolve' => function ($result) {
@@ -217,14 +231,14 @@ class _ContentSearchResultType extends ObjectType
'type' => TypeRegistry::boolean(),
'description' => "Is this result a comment?",
'resolve' => function ($result) {
return \in_array( 'IPS\Content\Comment', class_parents( self::getFieldValue('index_class', $result) ) ) && !self::getFieldValue('index_title', $result);
return in_array( 'IPS\Content\Comment', class_parents( self::getFieldValue('index_class', $result) ) ) && !self::getFieldValue('index_title', $result);
}
],
'isReview' => [
'type' => TypeRegistry::boolean(),
'description' => "Is this result a review?",
'resolve' => function ($result) {
return \in_array( 'IPS\Content\Review', class_parents( self::getFieldValue('index_class', $result) ) );
return in_array( 'IPS\Content\Review', class_parents( self::getFieldValue('index_class', $result) ) );
}
],
'replies' => [
@@ -245,14 +259,14 @@ class _ContentSearchResultType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'description' => "Author of this result",
'resolve' => function ($result) {
return \IPS\Member::load( self::getFieldValue('index_author', $result) );
return Member::load( self::getFieldValue('index_author', $result) );
}
],
'itemAuthor' => [
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'description' => "Author of the original content item",
'resolve' => function ($result) {
return \IPS\Member::load( self::getFieldValue('index_item_author', $result) );
return Member::load( self::getFieldValue('index_item_author', $result) );
}
],
'club' => [
@@ -269,13 +283,13 @@ class _ContentSearchResultType extends ObjectType
try {
$reactions = array();
if( \count( $result->reactions ) )
if( count( $result->reactions ) )
{
foreach( $result->reactions as $reactID => $count )
{
$reactions[] = array(
'id' => $reactID,
'reaction' => \IPS\Content\Reaction::load( $reactID ),
'reaction' => Reaction::load( $reactID ),
'count' => $count
);
}
@@ -283,7 +297,7 @@ class _ContentSearchResultType extends ObjectType
return $reactions;
}
catch (\Exception $e)
catch (Exception $e)
{}
return array();
@@ -305,15 +319,17 @@ class _ContentSearchResultType extends ObjectType
/**
* Return the number of replies
*
* @param \IPS\Content\Search\Result Search result
* @return string
* @param Result $result Search result
* @return int|null
*/
protected static function replies($result) {
protected static function replies( Result $result) : ?int
{
$asArray = $result->asArray();
$itemData = $asArray['itemData'];
if ( \in_array( 'IPS\Content\Comment', class_parents( $asArray['indexData']['index_class'] ) ) )
if ( in_array( 'IPS\Content\Comment', class_parents( $asArray['indexData']['index_class'] ) ) )
{
/* @var Item $itemClass */
$itemClass = static::getItemClass( $asArray );
if( isset( $itemClass::$databaseColumnMap['num_comments'] ) and isset( $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['num_comments'] ] ) )
@@ -344,8 +360,6 @@ class _ContentSearchResultType extends ObjectType
{
return $itemData[ $indexClass::$databasePrefix . $indexClass::$databaseColumnMap['num_comments'] ];
}
return 0;
}
}
@@ -355,12 +369,16 @@ class _ContentSearchResultType extends ObjectType
/**
* Return the content item title
*
* @param \IPS\Content\Search\Result Search result
* @return string
* @param Result $result Search result
* @return string|null
*/
protected static function title( $result )
protected static function title( Result $result ) : ?string
{
$asArray = $result->asArray();
/* @var Item $itemClass
* @var array $databaseColumnMap
*/
$itemClass = static::getItemClass( $asArray );
// If the content is used as the title (e.g. status updates), don't return anything
@@ -375,19 +393,21 @@ class _ContentSearchResultType extends ObjectType
/**
* Return the item URL
*
* @param \IPS\Content\Search\Result Search result
* @return string
* @param Result $result Search result
* @return Url
*/
protected static function url( $result )
protected static function url( Result $result ) : Url
{
$asArray = $result->asArray();
/* @var Item $itemClass */
$itemClass = static::getItemClass( $asArray );
$itemUrl = $itemClass::urlFromIndexData( $asArray['indexData'], $asArray['itemData'] );
/* Object URL */
if ( \in_array( 'IPS\Content\Comment', class_parents( $asArray['indexData']['index_class'] ) ) )
if ( in_array( 'IPS\Content\Comment', class_parents( $asArray['indexData']['index_class'] ) ) )
{
if ( \in_array( 'IPS\Content\Review', class_parents( $asArray['indexData']['index_class'] ) ) )
if ( in_array( 'IPS\Content\Review', class_parents( $asArray['indexData']['index_class'] ) ) )
{
$itemUrl = $itemUrl->setQueryString( array( 'do' => 'findReview', 'review' => $asArray['indexData']['index_object_id'] ) );
}
@@ -403,21 +423,23 @@ class _ContentSearchResultType extends ObjectType
/**
* Return the unread status of this content
*
* @param \IPS\Content\Search\Result Search result
* @param Result $result Search result
* @return boolean
*/
protected static function isUnread( $result )
protected static function isUnread( Result $result ) : bool
{
$asArray = $result->asArray();
$itemClass = static::getItemClass( $asArray );
if ( \in_array( 'IPS\Content\Comment', class_parents( $itemClass ) ) )
if ( in_array( 'IPS\Content\Comment', class_parents( $itemClass ) ) )
{
$contentClass = $itemClass::$contentItemClass;
/* @var Comment $itemClass */
$contentClass = $itemClass::$itemClass;
$unread = $contentClass::unreadFromData( NULL, $asArray['indexData']['index_date_updated'], $asArray['indexData']['index_date_created'], $asArray['indexData']['index_item_id'], $asArray['indexData']['index_container_id'], FALSE );
}
else
{
/* @var Item $itemClass */
$unread = $itemClass::unreadFromData( NULL, $asArray['indexData']['index_date_updated'], $asArray['indexData']['index_date_created'], $asArray['indexData']['index_item_id'], $asArray['indexData']['index_container_id'], FALSE );
}
@@ -427,13 +449,13 @@ class _ContentSearchResultType extends ObjectType
/**
* Resolve contentImage
*
* @param array Array representation of \IPS\Content\Search\Result
* @param array Arguments passed from resolver
* @return string
* @param Result $result Array representation of \IPS\Content\Search\Result
* @return array|null
*/
protected static function contentImages( $result )
protected static function contentImages( Result $result ) : ?array
{
try {
/* @var Item $itemClass */
$itemClass = static::getItemClass( $result->asArray() );
$item = $itemClass::load( self::getFieldValue('index_item_id', $result) );
$toReturn = array();
@@ -444,29 +466,28 @@ class _ContentSearchResultType extends ObjectType
{
foreach( $image as $extension => $file )
{
$toReturn[] = (string) \IPS\File::get( $extension, $file )->url;
$toReturn[] = (string) File::get( $extension, $file )->url;
}
}
}
return $toReturn;
} catch (\Exception $e) {
} catch (Exception $e) {
return NULL;
}
return NULL;
}
/**
* Resolve containerTitle
*
* @param array Array representation of \IPS\Content\Search\Result
* @param array Arguments passed from resolver
* @param Result $result Array representation of \IPS\Content\Search\Result
* @return string
*/
protected static function containerTitle( $result )
protected static function containerTitle( Result $result ) : string
{
$result = $result->asArray();
/* @var Item $itemClass */
$itemClass = static::getItemClass( $result );
$containerTitle = NULL;
@@ -482,15 +503,14 @@ class _ContentSearchResultType extends ObjectType
/**
* Resolve club
*
* @param array Array representation of \IPS\Content\Search\Result
* @param array Arguments passed from resolver
* @return \IPS\Member\Club
* @param Result $result Array representation of \IPS\Content\Search\Result
* @return Club|null
*/
protected static function club( $result )
protected static function club( Result $result ) : ?Club
{
if( self::getFieldValue('index_club_id', $result) )
{
return \IPS\Member\Club::load( self::getFieldValue('index_club_id', $result) );
return Club::load( self::getFieldValue('index_club_id', $result) );
}
return NULL;
@@ -499,14 +519,12 @@ class _ContentSearchResultType extends ObjectType
/**
* Get the item class for the result
*
* @param array Array representation of \IPS\Content\Search\Result
* @param Result $result Array representation of \IPS\Content\Search\Result
* @return string
*/
protected static function getItemClass( $result )
protected static function getItemClass( Result $result ) : string
{
$indexClass = $result['indexData']['index_class'];
$itemClass = ( \in_array( 'IPS\Content\Comment', class_parents( $indexClass ) ) ) ? $indexClass::$itemClass : $indexClass;
return $itemClass;
return ( in_array( 'IPS\Content\Comment', class_parents( $indexClass ) ) ) ? $indexClass::$itemClass : $indexClass;
}
}
@@ -12,23 +12,27 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Db;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Settings;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* GroupType for GraphQL API
*/
class _GroupType extends ObjectType
class GroupType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -40,7 +44,7 @@ class _GroupType extends ObjectType
'id' => [
'type' => TypeRegistry::int(),
'description' => "Group ID",
'resolve' => function ($group, $args, $context, $info) {
'resolve' => function ($group) {
return $group->g_id;
}
],
@@ -50,12 +54,12 @@ class _GroupType extends ObjectType
'values' => ['GUEST', 'MEMBER', 'ADMIN']
]),
'description' => "Is this a guest, member or admin group?",
'resolve' => function ($group, $args, $context, $info) {
if( $group->g_id == \IPS\Settings::i()->guest_group )
'resolve' => function ($group) {
if( $group->g_id == Settings::i()->guest_group )
{
return 'GUEST';
}
elseif( isset( \IPS\Member::administrators()['g'][ $group->g_id ] ) )
elseif( isset( Member::administrators()['g'][ $group->g_id ] ) )
{
return 'ADMIN';
}
@@ -74,21 +78,21 @@ class _GroupType extends ObjectType
'defaultValue' => FALSE
]
],
'resolve' => function ($group, $args, $context, $info) {
'resolve' => function ($group, $args) {
return ( $args['formatted'] ) ? $group->get_formattedName() : $group->name;
}
],
'canAccessSite' => [
'type' => TypeRegistry::boolean(),
'description' => "Can users in this group access the site?",
'resolve' => function ($group, $args, $context, $info) {
'resolve' => function ($group) {
return $group->g_view_board;
}
],
'canAccessOffline' => [
'type' => TypeRegistry::boolean(),
'description' => "Can users in this group access the site when it's offline?",
'resolve' => function ($group, $args, $context, $info) {
'resolve' => function ($group) {
return $group->g_access_offline;
}
],
@@ -96,14 +100,14 @@ class _GroupType extends ObjectType
'type' => TypeRegistry::boolean(),
'description' => "Can users in this group tag content (if enabled)?",
'resolve' => function ($group) {
return !$group->g_id || !\IPS\Settings::i()->tags_enabled ? FALSE : !( $group->g_bitoptions['gbw_disable_tagging'] );
return ( !$group->g_id || !Settings::i()->tags_enabled ) ? FALSE : !( $group->g_bitoptions['gbw_disable_tagging'] );
}
],
'maxMessengerRecipients' => [
'type' => TypeRegistry::int(),
'description' => "Maximum number of recipients to a PM sent by a member in this group",
'resolve' => function ($group) {
return \IPS\Member::loggedIn()->group['g_max_mass_pm'];
return Member::loggedIn()->group['g_max_mass_pm'];
}
],
'members' => [
@@ -119,7 +123,7 @@ class _GroupType extends ObjectType
'defaultValue' => 25
]
],
'resolve' => function ($group, $args, $context, $info) {
'resolve' => function ($group, $args) {
/* If we don't allow filtering by this group, don't return the members in it */
if( $group->g_bitoptions['gbw_hide_group'] )
{
@@ -128,7 +132,7 @@ class _GroupType extends ObjectType
$offset = max( $args['offset'], 0 );
$limit = min( $args['limit'], 50 );
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array('member_group_id=?', $group->g_id), NULL, array( $offset, $limit ) ), 'IPS\Member' );
return new ActiveRecordIterator( Db::i()->select( '*', 'core_members', array('member_group_id=?', $group->g_id), NULL, array( $offset, $limit ) ), 'IPS\Member' );
}
]
];
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* IgnoreOptionType for GraphQL API
*/
class _IgnoreOptionType extends ObjectType
class IgnoreOptionType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -62,7 +63,7 @@ class _IgnoreOptionType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "The language string for this ignore type",
'resolve' => function ($ignore) {
return \IPS\Member::loggedIn()->language()->addToStack( 'ignore_' . $ignore['type'] );
return Member::loggedIn()->language()->addToStack( 'ignore_' . $ignore['type'] );
}
]
];
@@ -10,25 +10,29 @@
*/
namespace IPS\core\api\GraphQL\Types;
use Exception;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use function defined;
use function strtolower;
use function strtoupper;
use function substr;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* LanguageType for GraphQL API
*/
class _LanguageType extends ObjectType
class LanguageType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -57,7 +61,7 @@ class _LanguageType extends ObjectType
'resolve' => function ($lang) {
if ( preg_match( '/^\w{2}[-_]\w{2}($|\.)/i', $lang->short ) )
{
return \strtolower( \substr( $lang->short, 0, 2 ) ) . '-' . \strtoupper( \substr( $lang->short, 3, 2 ) );
return strtolower( substr( $lang->short, 0, 2 ) ) . '-' . strtoupper( substr( $lang->short, 3, 2 ) );
}
return '';
@@ -91,12 +95,12 @@ class _LanguageType extends ObjectType
'type' => TypeRegistry::nonNull( TypeRegistry::string() )
]
],
'resolve' => function ($lang, $args, $context, $info) {
'resolve' => function ($lang, $args) {
try
{
return $lang->addToStack( $args['key'] );
}
catch (\Exception $err)
catch (Exception $err)
{
return $args['key'];
}
@@ -12,23 +12,27 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Session;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* LoginType for GraphQL API
*/
class _LoginType extends ObjectType
class LoginType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -48,14 +52,14 @@ class _LoginType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Login name",
'resolve' => function ($login) {
return \IPS\Member::loggedIn()->language()->get( $login->getTitle() );
return Member::loggedIn()->language()->get( $login->getTitle() );
}
],
'text' => [
'type' => TypeRegistry::string(),
'description' => "Login button text",
'resolve' => function ($login) {
return \IPS\Member::loggedIn()->language()->get( $login->buttonText() );
return Member::loggedIn()->language()->get( $login->buttonText() );
}
],
'icon' => [
@@ -76,8 +80,8 @@ class _LoginType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "URL to visit to authorize with this handler",
'resolve' => function ($login) {
$loginClass = new \IPS\Login;
return (string) \IPS\Http\Url::internal( $loginClass->url )->setQueryString('csrfKey', \IPS\Session::i()->csrfKey)->setQueryString('_processLogin', $login->_id);
$loginClass = new Login;
return (string) Url::internal( $loginClass->url )->setQueryString('csrfKey', Session::i()->csrfKey)->setQueryString('_processLogin', $login->_id);
}
]
];
@@ -11,24 +11,43 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\Fields\CoverPhotoField;
use IPS\Api\GraphQL\SafeException;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\Content\Search\Query;
use IPS\Content\Search\Results;
use IPS\core\Ignore;
use IPS\DateTime;
use IPS\Db;
use IPS\forums\Topic;
use IPS\Helpers\Form\Editor;
use IPS\Member;
use IPS\Member\Club;
use IPS\Member\Group;
use IPS\Notification\Api;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Settings;
use LogicException;
use OutOfRangeException;
use UnderflowException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MemberrType for GraphQL API
*/
class _MemberType extends ObjectType
class MemberType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -40,14 +59,14 @@ class _MemberType extends ObjectType
'id' => [
'type' => TypeRegistry::id(),
'description' => "Returns the member's ID",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return $member->member_id;
}
],
'email' => [
'type' => TypeRegistry::string(),
'description' => "Returns the member's email address (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isAuthorized($member) )
{
return NULL;
@@ -58,7 +77,7 @@ class _MemberType extends ObjectType
'url' => [
'type' => TypeRegistry::string(),
'description' => "Returns the URL to member's profile",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return (string) $member->url();
}
],
@@ -71,21 +90,21 @@ class _MemberType extends ObjectType
'defaultValue' => FALSE
]
],
'resolve' => function( $member, $args, $context, $info ) {
return ( $args['formatted'] ) ? \IPS\Member\Group::load( $member->member_group_id )->formatName( $member->name ) : $member->name;
'resolve' => function( $member, $args ) {
return ( $args['formatted'] ) ? Group::load( $member->member_group_id )->formatName( $member->name ) : $member->name;
}
],
'title' => [
'type' => TypeRegistry::string(),
'description' => "Returns the member's title",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return null;
}
],
'timezone' => [
'type' => TypeRegistry::string(),
'description' => "Returns the member's timezone (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isAuthorized($member) )
{
return NULL;
@@ -96,7 +115,7 @@ class _MemberType extends ObjectType
'joined' => [
'type' => TypeRegistry::int(),
'description' => "Returns the date the member joined",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return $member->joined->getTimestamp();
}
],
@@ -131,7 +150,7 @@ class _MemberType extends ObjectType
'defaultValue' => NULL
]
],
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member, $args, $context ) {
if( !self::isOwnerMember($member) )
{
return NULL;
@@ -142,7 +161,7 @@ class _MemberType extends ObjectType
'notificationCount' => [
'type' => TypeRegistry::int(),
'description' => "Returns the member's notification count (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isOwnerMember($member) )
{
return NULL;
@@ -153,41 +172,40 @@ class _MemberType extends ObjectType
'posts' => [
'type' => TypeRegistry::int(),
'description' => "Returns the member's post count",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return $member->member_posts;
}
],
'contentCount' => [
'type' => TypeRegistry::int(),
'description' => "Returns the member's post count",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
return $member->member_posts;
}
],
'reputationCount' => [
'type' => TypeRegistry::int(),
'description' => "Returns the member's reputation count",
'resolve' => function( $member, $args, $context, $info ) {
return ( \IPS\Settings::i()->reputation_enabled ) ? $member->pp_reputation_points : null;
'resolve' => function( $member ) {
return ( Settings::i()->reputation_enabled ) ? $member->pp_reputation_points : null;
}
],
'solvedCount' => [
'type' => TypeRegistry::int(),
'description' => "Returns the number of items this member has solved",
'resolve' => function ($member) {
if( !\IPS\Application::appIsEnabled('forums') || !\IPS\forums\Topic::anyContainerAllowsSolvable() )
if( !Application::appIsEnabled('forums') || !Topic::anyContainerAllowsSolvable() )
{
return 0;
}
$count = \IPS\Db::i()->select( 'COUNT(*) as count', 'core_solved_index', array( 'member_id=?', $member->member_id ) )->first();
return $count;
return (int) Db::i()->select( 'COUNT(*) as count', 'core_solved_index', array( 'member_id=? AND type=?', $member->member_id, 'solved' ) )->first();
}
],
'ip_address' => [
'type' => TypeRegistry::string(),
'description' => "Returns the member's IP address (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isAuthorized($member) )
{
return NULL;
@@ -198,7 +216,7 @@ class _MemberType extends ObjectType
'warn_level' => [
'type' => TypeRegistry::int(),
'description' => "Returns the member's warning level (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isAuthorized($member) ){
return NULL;
}
@@ -208,7 +226,7 @@ class _MemberType extends ObjectType
'profileViews' => [
'type' => TypeRegistry::int(),
'description' => "Returns the number of profile views for this member (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
'resolve' => function( $member ) {
if( !self::isAuthorized($member) )
{
return NULL;
@@ -231,35 +249,35 @@ class _MemberType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::group(),
'description' => "Returns the member's primary group",
'resolve' => function( $member, $args, $context, $info ) {
return \IPS\Member\Group::load( $member->member_group_id );
return Group::load( $member->member_group_id );
}
],
'isOnline' => [
'type' => TypeRegistry::boolean(),
'description' => "Indicates whether the user is online, taking permissions into account",
'resolve' => function ($member) {
return ( $member->isOnline() AND !$member->isOnlineAnonymously() ) OR ( $member->isOnlineAnonymously() AND \IPS\Member::loggedIn()->isAdmin() );
return ( $member->isOnline() AND !$member->isOnlineAnonymously() ) OR ( $member->isOnlineAnonymously() AND Member::loggedIn()->isAdmin() );
}
],
'lastActivity' => [
'type' => TypeRegistry::string(),
'description' => "Returns the timestamp of the member's last activity",
'resolve' => function( $member, $args, $context, $info ) {
return \IPS\DateTime::ts( $member->last_activity )->rfc3339();
return DateTime::ts( $member->last_activity )->rfc3339();
}
],
'lastVisit' => [
'type' => TypeRegistry::string(),
'description' => "Returns the timestamp of the member's last visit",
'resolve' => function( $member, $args, $context, $info ) {
return \IPS\DateTime::ts( $member->last_visit )->rfc3339();
return DateTime::ts( $member->last_visit )->rfc3339();
}
],
'lastPost' => [
'type' => TypeRegistry::string(),
'description' => "Returns the timestamp of member's last post",
'resolve' => function( $member, $args, $context, $info ) {
return \IPS\DateTime::ts( $this->member_last_post )->rfc3339();
return DateTime::ts( $member->member_last_post )->rfc3339();
}
],
'secondaryGroups' => [
@@ -325,7 +343,7 @@ class _MemberType extends ObjectType
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::club() ),
'description' => "Returns the member's clubs (depending on permissions)",
'resolve' => function( $member, $args, $context, $info ) {
return \IPS\Member\Club::clubs( $member, 25, 'last_activity' );
return Club::clubs( $member, 25, 'last_activity' );
}
],
'photo' => [
@@ -335,7 +353,7 @@ class _MemberType extends ObjectType
return self::photo($member);
}
],
'coverPhoto' => \IPS\Api\GraphQL\Fields\CoverPhotoField::getDefinition('core_MemberCoverPhoto'),
'coverPhoto' => CoverPhotoField::getDefinition('core_MemberCoverPhoto'),
'customFieldGroups' => [
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::profileFieldGroup() ),
'resolve' => function( $member, $args, $context, $info ) {
@@ -348,10 +366,10 @@ class _MemberType extends ObjectType
'resolve' => function( $member ) {
if( $member->member_id && !self::isOwnerMember( $member ) )
{
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_USER', '2S401/1', 403 );
throw new SafeException( 'INVALID_USER', '2S401/1', 403 );
}
return \IPS\Helpers\Form\Editor::maxTotalAttachmentSize( $member, 0 );
return Editor::maxTotalAttachmentSize( $member, 0 );
}
],
'canBeIgnored' => [
@@ -399,10 +417,10 @@ class _MemberType extends ObjectType
/**
* Return a member's photo
*
* @param \IPS\Member
* @param Member $member
* @return string
*/
protected static function photo($member)
protected static function photo( Member $member ) : string
{
$member_properties = array();
@@ -411,7 +429,7 @@ class _MemberType extends ObjectType
$member_properties[ $column ] = $member->$column;
}
$photoUrl = \IPS\Member::photoUrl( $member_properties );
$photoUrl = Member::photoUrl( $member_properties );
if( mb_strpos( $photoUrl, "data:image/svg+xml" ) === 0 )
{
@@ -426,12 +444,12 @@ class _MemberType extends ObjectType
/**
* Return a json array containing letter/color combo for a user's letter photo
*
* @param \IPS\Member
* @param Member $member
* @return string
*/
protected static function getLetterPhotoData($member)
protected static function getLetterPhotoData( Member $member ) : string
{
return json_encode( \IPS\Member::generateLetterPhoto( array(
return json_encode( Member::generateLetterPhoto( array(
'name' => $member->name,
'pp_main_photo' => $member->pp_main_photo,
'member_id' => $member->member_id
@@ -441,32 +459,33 @@ class _MemberType extends ObjectType
/**
* Determines if this is a user authorized to access sensitive data
*
* @param \IPS\Member
* @param Member $member
* @return boolean
*/
protected static function isAuthorized($member)
protected static function isAuthorized( Member $member ) : bool
{
return self::isOwnerMember($member) || \IPS\Member::loggedIn()->isAdmin();
return self::isOwnerMember($member) || Member::loggedIn()->isAdmin();
}
/**
* Determines if this user is the same as the user requesting the info
*
* @param \IPS\Member
* @param Member $member
* @return boolean
*/
protected static function isOwnerMember($member)
protected static function isOwnerMember( Member $member ) : bool
{
return $member->member_id && $member->member_id == \IPS\Member::loggedIn()->member_id;
return $member->member_id && $member->member_id == Member::loggedIn()->member_id;
}
/**
* Returns a member's content
*
* @param \IPS\Member
* @return boolean
* @param Member $member
* @param array $args
* @return Results
*/
protected static function content($member, $args)
protected static function content( Member $member, array $args) : Results
{
// Get page
// We don't know the count at this stage, so figure out the page number from
@@ -480,8 +499,8 @@ class _MemberType extends ObjectType
$page = floor( $offset / $limit ) + 1;
}
$latestActivity = \IPS\Content\Search\Query::init()->filterForProfile( $member )->setLimit( $limit )->setPage( $page )->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_CREATED )->search();
$latestActivity->init( TRUE );
$latestActivity = Query::init()->filterForProfile( $member )->setLimit( $limit )->setPage( $page )->setOrder( Query::ORDER_NEWEST_CREATED )->search();
$latestActivity->init();
return $latestActivity;
}
@@ -489,20 +508,20 @@ class _MemberType extends ObjectType
/**
* Return user's notifications
*
* @param \IPS\Member
* @param Member $member
* @param array $args
* @param array $context
* @return array
*/
protected static function notifications($member, $args, $context)
protected static function notifications( Member $member, array $args, array $context ) : array
{
/* Specify filter in where clause */
$where = array();
$where[] = array( "notification_app IN('" . implode( "','", array_keys( \IPS\Application::enabledApplications() ) ) . "')" );
$where[] = array( "`member`=?", \IPS\Member::loggedIn()->member_id );
$where[] = array( "notification_app IN('" . implode( "','", array_keys( Application::enabledApplications() ) ) . "')" );
$where[] = array( "`member`=?", Member::loggedIn()->member_id );
/* Are we filtering by unread status? */
if( isset( $args['unread'] ) && $args['unread'] !== NULL )
if( isset( $args['unread'] ) )
{
if( $args['unread'] === TRUE )
{
@@ -523,21 +542,21 @@ class _MemberType extends ObjectType
}
/* Get Count */
$count = \IPS\Db::i()->select( 'COUNT(*) as cnt', 'core_notifications', $where )->first();
$count = Db::i()->select( 'COUNT(*) as cnt', 'core_notifications', $where )->first();
/* Get results */
$returnRows = array();
$offset = max( $args['offset'], 0 );
$limit = min( $args['limit'], 50 );
foreach( \IPS\Db::i()->select( '*', 'core_notifications', $where, $sort, array( $offset, $limit ) ) as $row )
foreach( Db::i()->select( '*', 'core_notifications', $where, $sort, array( $offset, $limit ) ) as $row )
{
try
{
$notification = \IPS\Notification\Api::constructFromData( $row );
$notification = Api::constructFromData( $row );
$returnRows[] = array( 'notification' => $notification, 'data' => $notification->getData( FALSE ) );
}
catch ( \LogicException $e ) { }
catch ( LogicException $e ) { }
}
return $returnRows;
@@ -546,17 +565,19 @@ class _MemberType extends ObjectType
/**
* Return custom profile field groups
*
* @param \IPS\Member
* @return boolean
* @param Member $member
* @param array $args
* @param array $context
* @return array|null
*/
protected static function customFieldGroups($member, $args, $context)
protected static function customFieldGroups( Member $member, array $args, array $context ) : ?array
{
/* Get profile field values */
try
{
$profileFieldValues = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
$profileFieldValues = Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
return null;
}
@@ -565,11 +586,11 @@ class _MemberType extends ObjectType
{
$fields = array();
if( \IPS\Member::loggedIn()->isAdmin() OR \IPS\Member::loggedIn()->modPermissions() )
if( Member::loggedIn()->isAdmin() OR Member::loggedIn()->modPermissions() )
{
$where = array( "pfd.pf_member_hide='owner' OR pfd.pf_member_hide='staff' OR pfd.pf_member_hide='all'" );
}
elseif( \IPS\Member::loggedIn()->member_id == $member->member_id )
elseif( Member::loggedIn()->member_id == $member->member_id )
{
$where = array( "pfd.pf_member_hide='owner' OR pfd.pf_member_hide='all'" );
}
@@ -578,7 +599,7 @@ class _MemberType extends ObjectType
$where = array( "pfd.pf_member_hide='all'" );
}
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( 'pfd.*', array('core_pfields_data', 'pfd'), $where, 'pfg.pf_group_order, pfd.pf_position' )->join(
foreach( new ActiveRecordIterator( Db::i()->select( 'pfd.*', array('core_pfields_data', 'pfd'), $where, 'pfg.pf_group_order, pfd.pf_position' )->join(
array('core_pfields_groups', 'pfg'),
"pfd.pf_group_id=pfg.pf_group_id"
), 'IPS\core\ProfileFields\Field' ) as $field )
@@ -598,7 +619,7 @@ class _MemberType extends ObjectType
'id' => md5( $member->member_id . $field->id ),
'fieldId' => $field->id,
'title' => 'core_pfield_' . $field->id,
'value' => json_encode( $field->apiValue( $profileFieldValues['field_' . $field->id] ) ),
'value' => json_encode( $field->apiValue( $profileFieldValues['field_' . $field->id], true ) ),
'type' => $field->type
);
}
@@ -613,18 +634,20 @@ class _MemberType extends ObjectType
/**
* Resolve followers field
*
* @param \IPS\Member
* @param array Arguments passed to this resolver
* @param Member $member
* @param array $args Arguments passed to this resolver
* @param array $context
* @param array $info
* @return array
*/
protected static function followers($member, $args, $context, $info)
protected static function followers( Member $member, array $args, array $context, array $info ) : array
{
$limit = min( $args['limit'], 50 );
return array_map(
function ($followRow)
{
return \IPS\Member::load( $followRow['follow_member_id'] );
return Member::load( $followRow['follow_member_id'] );
},
iterator_to_array( $member->followers( 3, array( 'immediate', 'daily', 'weekly' ), NULL, array(0, $limit ) ) )
);
@@ -633,20 +656,22 @@ class _MemberType extends ObjectType
/**
* Resolve secondary groups field
*
* @param \IPS\Member
* @param array Arguments passed to this resolver
* @return array
* @param Member $member
* @param array $args
* @param array $context
* @param array $info
* @return array
*/
protected static function secondaryGroups($member, $args, $context, $info)
protected static function secondaryGroups( Member $member, array $args, array $context, array $info ) : array
{
$secondaryGroups = array();
foreach ( array_filter( array_map( "intval", explode( ',', $member->mgroup_others ) ) ) as $secondaryGroupId )
{
try
{
$secondaryGroups[] = \IPS\Member\Group::load( $secondaryGroupId );
$secondaryGroups[] = Group::load( $secondaryGroupId );
}
catch ( \OutOfRangeException $e ) { }
catch ( OutOfRangeException $e ) { }
}
return $secondaryGroups;
@@ -655,12 +680,12 @@ class _MemberType extends ObjectType
/**
* Resolve ignore status field
*
* @param \IPS\Member $member Member to check
* @return array
* @param Member $member Member to check
* @return array|null
*/
protected static function ignoreStatus( \IPS\Member $member)
protected static function ignoreStatus( Member $member) : ?array
{
if( !$member->canBeIgnored() || $member->member_id === \IPS\Member::loggedIn()->member_id ){
if( !$member->canBeIgnored() || $member->member_id === Member::loggedIn()->member_id ){
return NULL;
}
@@ -668,15 +693,15 @@ class _MemberType extends ObjectType
try
{
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
$ignore = Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', Member::loggedIn()->member_id ) );
}
catch ( \OutOfRangeException $e ) {
catch ( OutOfRangeException $e ) {
// Just keep ignore as false
}
$ignoreStatus = array();
foreach ( \IPS\core\Ignore::types() as $type )
foreach ( Ignore::types() as $type )
{
$ignoreStatus[] = array(
'type' => $type,
@@ -687,7 +712,7 @@ class _MemberType extends ObjectType
return $ignoreStatus;
}
public static function getOrderByOptions()
public static function getOrderByOptions() : array
{
return ['member_id', 'joined','last_activity','name','last_visit'];
}
@@ -12,40 +12,42 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Api\GraphQL\ItemType;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MessengerConversation for GraphQL API
*/
class _MessengerConversationType extends \IPS\Content\Api\GraphQL\ItemType
class MessengerConversationType extends ItemType
{
/*
* @brief The item classname we use for this type
*/
protected static $itemClass = '\IPS\core\Messenger\Conversation';
protected static string $itemClass = '\IPS\core\Messenger\Conversation';
/*
* @brief GraphQL type name
*/
protected static $typeName = 'core_MessengerConversation';
protected static string $typeName = 'core_MessengerConversation';
/*
* @brief GraphQL type description
*/
protected static $typeDescription = 'A messenger conversation';
protected static string $typeDescription = 'A messenger conversation';
/**
* Return the fields available in this type
*
* @return array
*/
public function fields()
public function fields(): array
{
$defaultFields = parent::fields();
$conversationFields = array(
@@ -130,9 +132,9 @@ class _MessengerConversationType extends \IPS\Content\Api\GraphQL\ItemType
* Here we adjust the resolver for the commentInformation field to check whether this is
* a poll-only topic.
*
* @return string|null
* @return array
*/
public static function getItemPermissionFields()
public static function getItemPermissionFields(): array
{
$defaultFields = parent::getItemPermissionFields();
@@ -149,7 +151,7 @@ class _MessengerConversationType extends \IPS\Content\Api\GraphQL\ItemType
*
* @return array
*/
public static function getOrderByOptions()
public static function getOrderByOptions(): array
{
// Note: these options all get prefixed with 'mt_' when passed into the query.
return array('last_post_time', 'start_time', 'replies');
@@ -160,21 +162,8 @@ class _MessengerConversationType extends \IPS\Content\Api\GraphQL\ItemType
*
* @return ObjectType
*/
protected static function getCommentType()
protected static function getCommentType(): ObjectType
{
return \IPS\core\api\GraphQL\TypeRegistry::messengerReply();
}
/**
* Resolve the comments field - overridden from ItemType
* If this is a question and order isn't date, then force order by votes
*
* @param \IPS\forums\Topic
* @param array Arguments passed to this resolver
* @return array
*/
/*protected static function comments($topic, $args)
{
return parent::comments($topic, $args);
}*/
}
@@ -12,26 +12,29 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Db;
use IPS\Http\Url;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MessengerFolder for GraphQL API
*/
class _MessengerFolderType extends ObjectType
class MessengerFolderType extends ObjectType
{
public $memberFolders = NULL;
public ?array $memberFolders = NULL;
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -81,7 +84,7 @@ class _MessengerFolderType extends ObjectType
* @Param boolean $refetch If true, will force folder data to be refetched rather than served from cached values
* @return array
*/
public function getMemberFolders($refetch = FALSE)
public function getMemberFolders( bool $refetch = FALSE ) : array
{
if( $this->memberFolders !== NULL && !$refetch )
{
@@ -89,15 +92,15 @@ class _MessengerFolderType extends ObjectType
}
/* Get folders */
$folders = array( 'myconvo' => \IPS\Member::loggedIn()->language()->addToStack('messenger_folder_inbox') );
if ( \IPS\Member::loggedIn()->pconversation_filters )
$folders = array( 'myconvo' => Member::loggedIn()->language()->addToStack('messenger_folder_inbox') );
if ( Member::loggedIn()->pconversation_filters )
{
$folders = $folders + array_filter( json_decode( \IPS\Member::loggedIn()->pconversation_filters, TRUE ) );
$folders = $folders + array_filter( json_decode( Member::loggedIn()->pconversation_filters, TRUE ) );
}
/* What are our folder counts? */
/* Note: The setKeyField and setValueField calls here were causing the folder counts to be incorrect (if you had two folders with 1 message in each, then both showed a count of 2) */
$counts = iterator_to_array( \IPS\Db::i()->select( 'map_folder_id, count(*) as count', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', \IPS\Member::loggedIn()->member_id ), NULL, NULL, 'map_folder_id' ) );
$counts = iterator_to_array( Db::i()->select( 'map_folder_id, count(*) as count', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', Member::loggedIn()->member_id ), NULL, NULL, 'map_folder_id' ) );
$folderCounts = array();
foreach( $counts AS $k => $count )
{
@@ -109,8 +112,8 @@ class _MessengerFolderType extends ObjectType
$this->memberFolders[ $id ] = array(
'id' => $id,
'name' => $name,
'url' => \IPS\Http\Url::internal( "app=core&module=messaging&controller=messenger", 'front', 'messaging' )->setQueryString('folder', $id),
'count' => isset( $folderCounts[ $id ] ) ? $folderCounts[ $id ] : 0
'url' => Url::internal( "app=core&module=messaging&controller=messenger", 'front', 'messaging' )->setQueryString('folder', $id),
'count' => $folderCounts[$id] ?? 0
);
}
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MessengerParticipant for GraphQL API
*/
class _MessengerParticipantType extends ObjectType
class MessengerParticipantType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -50,7 +51,7 @@ class _MessengerParticipantType extends ObjectType
'resolve' => function ($map) {
if( $map['map_user_id'] )
{
return \IPS\Member::load( $map['map_user_id'] );
return Member::load( $map['map_user_id'] );
}
return NULL;
@@ -11,63 +11,44 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Api\GraphQL\CommentType;
use IPS\core\api\GraphQL\TypeRegistry;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MessengerReply for GraphQL API
*/
class _MessengerReplyType extends \IPS\Content\Api\GraphQL\CommentType
class MessengerReplyType extends CommentType
{
/*
* @brief The item classname we use for this type
*/
protected static $commentClass = '\IPS\core\Messenger\Message';
protected static string $commentClass = '\IPS\core\Messenger\Message';
/*
* @brief GraphQL type name
*/
protected static $typeName = 'core_MessengerReply';
protected static string $typeName = 'core_MessengerReply';
/*
* @brief GraphQL type description
*/
protected static $typeDescription = 'A reply';
protected static string $typeDescription = 'A reply';
/**
* Get the item type that goes with this item type
*
* @return ObjectType
*/
public static function getItemType()
public static function getItemType(): ObjectType
{
return \IPS\core\api\GraphQL\TypeRegistry::messengerConversation();
return TypeRegistry::messengerConversation();
}
/**
* Return the fields available in this type
*
* @return array
*/
/*public function fields()
{
}*/
/**
* Return the definite article, but without the item type
*
* @return string
*/
/*public static function definiteArticleNoItem($post, $options = array())
{
$type = $post->item()->isQuestion() ? 'answer_lc' : 'post_lc';
return \IPS\Member::loggedIn()->language()->addToStack($type, FALSE, $options);
}*/
}
@@ -12,23 +12,23 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* NotificationMethodType for GraphQL API
*/
class _NotificationMethodType extends ObjectType
class NotificationMethodType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -12,23 +12,23 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* NotificationType for GraphQL API
*/
class _NotificationType extends ObjectType
class NotificationType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -97,21 +97,21 @@ class _NotificationType extends ObjectType
'type' => \IPS\core\Api\GraphQL\TypeRegistry::member(),
'description' => "Member that triggered this notification",
'resolve' => function ($notification) {
return isset( $notification['data']['author'] ) ? $notification['data']['author'] : NULL;
return $notification['data']['author'] ?? NULL;
}
],
'title' => [
'type' => TypeRegistry::string(),
'description' => "Notification title",
'resolve' => function ($notification) {
return isset( $notification['data']['title'] ) ? $notification['data']['title'] : NULL;
return $notification['data']['title'] ?? NULL;
}
],
'content' => [
'type' => TypeRegistry::richText(),
'description' => "Notification content",
'resolve' => function ($notification) {
return isset( $notification['data']['content'] ) ? $notification['data']['content'] : NULL;
return $notification['data']['content'] ?? NULL;
}
],
'url' => [
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function count;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* NotificationTypeType for GraphQL API
*/
class _NotificationTypeType extends ObjectType
class NotificationTypeType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -55,7 +57,7 @@ class _NotificationTypeType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Title of the notification group",
'resolve' => function ($type) {
return \IPS\Member::loggedIn()->language()->addToStack( 'notifications__' . $type['extension'] );
return Member::loggedIn()->language()->addToStack( 'notifications__' . $type['extension'] );
}
],
'type' => [
@@ -76,14 +78,14 @@ class _NotificationTypeType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Type description",
'resolve' => function ($type) {
return \IPS\Member::loggedIn()->language()->addToStack( $type['description'] );
return Member::loggedIn()->language()->addToStack( $type['description'] );
}
],
'lang' => [
'type' => TypeRegistry::string(),
'description' => "The translated label",
'resolve' => function ($type) {
return \IPS\Member::loggedIn()->language()->addToStack( $type['name'] );
return Member::loggedIn()->language()->addToStack( $type['name'] );
}
],
'inline' => [
@@ -104,7 +106,7 @@ class _NotificationTypeType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::notificationMethod(),
'description' => "Push notification method",
'resolve' => function ($type) {
if ( !isset( $type['push'] ) || !\count( $type['push'] ) || !\IPS\Member::loggedIn()->members_bitoptions['mobile_notifications'] )
if ( !isset( $type['push'] ) || !count( $type['push'] ) || !Member::loggedIn()->members_bitoptions['mobile_notifications'] )
{
return NULL;
}
@@ -12,23 +12,26 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function array_slice;
use function defined;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PollQuestionType for GraphQL API
*/
class _PollQuestionType extends ObjectType
class PollQuestionType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -92,7 +95,7 @@ class _PollQuestionType extends ObjectType
'type' => TypeRegistry::boolean(),
'description' => "Whether the logged-in member voted for this choice",
'resolve' => function ($choice) {
return \in_array( \IPS\Member::loggedIn()->member_id, $choice['voters'] );
return in_array( Member::loggedIn()->member_id, $choice['voters'] );
}
],
'voters' => [
@@ -111,11 +114,11 @@ class _PollQuestionType extends ObjectType
$members = array();
$offset = max( $args['offset'], 0 );
$limit = min( $args['limit'], 50 );
$membersToLoad = \array_slice( $choice['voters'], $offset, $limit );
$membersToLoad = array_slice( $choice['voters'], $offset, $limit );
foreach( $membersToLoad as $memberID )
{
$members[] = \IPS\Member::load( $memberID );
$members[] = Member::load( $memberID );
}
return $members;
@@ -134,7 +137,7 @@ class _PollQuestionType extends ObjectType
'poll' => $question['poll'],
'title' => $choice,
'votes' => isset( $question['data']['votes'] ) ? $question['data']['votes'][ $idx ] : 0,
'voters' => isset( $question['voters'][ $idx ] ) ? $question['voters'][ $idx ] : array()
'voters' => $question['voters'][$idx] ?? array()
);
}
@@ -12,23 +12,28 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\DateTime;
use IPS\Db;
use IPS\Poll;
use function defined;
use function intval;
use function is_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PollType for GraphQL API
*/
class _PollType extends ObjectType
class PollType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -69,7 +74,7 @@ class _PollType extends ObjectType
'type' => TypeRegistry::int(),
'description' => "Timestamp of when this poll will close (NULL if no close date set)",
'resolve' => function ($poll) {
if( $poll->poll_close_date instanceof \IPS\DateTime )
if( $poll->poll_close_date instanceof DateTime )
{
return $poll->poll_close_date->getTimestamp();
}
@@ -136,10 +141,11 @@ class _PollType extends ObjectType
/**
* Resolve poll questions field
*
* @param \IPS\Poll
* @param Poll $poll
* @return array
*/
protected static function getQuestions ($poll) {
protected static function getQuestions ( Poll $poll ) : array
{
$questions = array();
foreach ( $poll->choices as $idx => $choice )
@@ -161,7 +167,7 @@ class _PollType extends ObjectType
// Get the voters for each choice in advance here, but we won't load the member
// until questions -> choices -> voters is resolved in PollQuestionType
$voters = array();
$query = \IPS\Db::i()->select( '*', 'core_voters', array( 'poll=?', $poll->pid ) );
$query = Db::i()->select( '*', 'core_voters', array( 'poll=?', $poll->pid ) );
foreach( $query as $voter )
{
@@ -170,16 +176,16 @@ class _PollType extends ObjectType
$voteData = json_decode( $voter['member_choices'] );
foreach( $voteData as $questionIdx => $answer )
{
if( \is_array( $answer ) )
if( is_array( $answer ) )
{
foreach( $answer as $_answer )
{
$questions[ $questionIdx ]['voters'][ \intval( $_answer ) ][] = $voter['member_id'];
$questions[ $questionIdx ]['voters'][ intval( $_answer ) ][] = $voter['member_id'];
}
}
else
{
$questions[ $questionIdx ]['voters'][ \intval( $answer ) ][] = $voter['member_id'];
$questions[ $questionIdx ]['voters'][ intval( $answer ) ][] = $voter['member_id'];
}
}
}
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PopularContributorsType for GraphQL API
*/
class _PopularContributorType extends ObjectType
class PopularContributorType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -38,19 +39,19 @@ class _PopularContributorType extends ObjectType
return [
'rep' => [
'type' => TypeRegistry::int(),
'resolve' => function ($contributor, $args) {
'resolve' => function ($contributor) {
return $contributor['rep'];
}
],
'user' => [
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'resolve' => function ($contributor, $args) {
'resolve' => function ($contributor) {
if( $contributor['member_id'] )
{
return \IPS\Member::load( $contributor['member_id'] );
return Member::load( $contributor['member_id'] );
}
return new \IPS\Member;
return new Member;
}
]
];
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* StreamType for GraphQL API
*/
class _ProfileFieldGroupType extends ObjectType
class ProfileFieldGroupType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -55,7 +56,7 @@ class _ProfileFieldGroupType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Profile field group title",
'resolve' => function ($fieldGroup) {
return \IPS\Member::loggedIn()->language()->get( $fieldGroup['title'] );
return Member::loggedIn()->language()->get( $fieldGroup['title'] );
}
],
'fields' => [
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ProfileFieldType for GraphQL API
*/
class _ProfileFieldType extends ObjectType
class ProfileFieldType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -56,7 +57,7 @@ class _ProfileFieldType extends ObjectType
'type' => TypeRegistry::string(),
'description' => "Profile field title",
'resolve' => function ($field) {
return \IPS\Member::loggedIn()->language()->get( $field['title'] );
return Member::loggedIn()->language()->get( $field['title'] );
}
],
'value' => [
@@ -13,23 +13,30 @@ namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\UnionType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content;
use IPS\Content\Comment;
use IPS\Content\Item;
use IPS\Content\Review;
use IPS\Member;
use IPS\Node\Model;
use function count;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PromotedItemType for GraphQL API
*/
class _PromotedItemType extends ObjectType
class PromotedItemType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -49,7 +56,7 @@ class _PromotedItemType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'description' => 'Who promoted this item',
'resolve' => function ($item) {
return \IPS\Member::load( $item->added_by );
return Member::load( $item->added_by );
}
],
'images' => [
@@ -58,7 +65,7 @@ class _PromotedItemType extends ObjectType
'resolve' => function ($item) {
$images = array();
if( \count( $item->imageObjects() ) )
if( count( $item->imageObjects() ) )
{
foreach( $item->imageObjects() as $file )
{
@@ -87,23 +94,24 @@ class _PromotedItemType extends ObjectType
'type' => new UnionType([
'name' => 'core_PromotedItem_TypeUnion',
'types' => [
\IPS\Content\Api\GraphQL\TypeRegistry::comment(),
\IPS\Content\Api\GraphQL\TypeRegistry::item(),
Content\Api\GraphQL\TypeRegistry::comment(),
Content\Api\GraphQL\TypeRegistry::item(),
\IPS\Node\Api\GraphQL\TypeRegistry::node()
],
'resolveType' => function ($item) {
if ( $item instanceof \IPS\Content\Comment )
if ( $item instanceof Comment )
{
return \IPS\Content\Api\GraphQL\TypeRegistry::comment();
return Content\Api\GraphQL\TypeRegistry::comment();
}
else if ( $item instanceof \IPS\Content\Item )
else if ( $item instanceof Item )
{
return \IPS\Content\Api\GraphQL\TypeRegistry::item();
return Content\Api\GraphQL\TypeRegistry::item();
}
else if( $item instanceof \IPS\Node\Model )
else if( $item instanceof Model )
{
return \IPS\Node\Api\GraphQL\TypeRegistry::node();
}
}
return null;
}
]),
'description' => 'The original item',
@@ -118,26 +126,23 @@ class _PromotedItemType extends ObjectType
]),
'description' => "What kind of content is this item?",
'resolve' => function ($item) {
if ( $item->object() instanceof \IPS\Content\Comment )
if ( $item->object() instanceof Comment )
{
return 'COMMENT';
}
else if ( $item->object() instanceof \IPS\Content\Review )
else if ( $item->object() instanceof Review )
{
return 'REVIEW';
}
else if ( $item->object() instanceof \IPS\Content )
else if ( $item->object() instanceof Content )
{
return 'ITEM';
}
else if ( $item->object() instanceof \IPS\Node\Model )
else if ( $item->object() instanceof Model )
{
return 'NODE';
}
print_r( $item->object() );
exit;
return NULL;
}
],
@@ -145,7 +150,7 @@ class _PromotedItemType extends ObjectType
'type' => TypeRegistry::string(),
'description' => 'Promoted blurb provided by staff',
'resolve' => function ($item) {
$text = trim( $item->getText('internal', false) );
$text = trim( $item->getText(false) );
if( $text ){
return $text;
@@ -164,8 +169,7 @@ class _PromotedItemType extends ObjectType
'reputation' => [
'type' => TypeRegistry::reputation(),
'resolve' => function ($item) {
$reactionClass = $item->objectReactionClass;
return $reactionClass;
return $item->objectReactionClass;
}
],
'dataCount' => [
@@ -12,23 +12,23 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ReportReasonType for GraphQL API
*/
class _ReportReasonType extends ObjectType
class ReportReasonType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ReportType for GraphQL API
*/
class _ReportType extends ObjectType
class ReportType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -98,10 +100,11 @@ class _ReportType extends ObjectType
/**
* Returns whether the user has reported this content
*
* @param Content $content
* @return boolean
*/
protected function hasReported($content)
protected function hasReported( Content $content ) : bool
{
return ( $content->canReport( \IPS\Member::loggedIn() ) === 'report_err_already_reported' );
return ( $content->canReport( Member::loggedIn() ) === 'report_err_already_reported' );
}
}
@@ -10,19 +10,21 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\UnionType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\api\GraphQL\TypeRegistry;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* @brief Search result union type
*/
class _SearchResultType extends UnionType
class SearchResultType extends UnionType
{
public function __construct()
{
@@ -30,17 +32,17 @@ class _SearchResultType extends UnionType
'name' => 'core_SearchResult',
'description' => 'A union type that returns either a member, or a content search result',
'types' => [
\IPS\core\api\GraphQL\TypeRegistry::member(),
\IPS\core\api\GraphQL\TypeRegistry::contentSearchResult()
TypeRegistry::member(),
TypeRegistry::contentSearchResult()
],
'resolveType' => function ($result) {
if ( $result instanceof \IPS\Member )
if ( $result instanceof Member )
{
return \IPS\core\api\GraphQL\TypeRegistry::member();
return TypeRegistry::member();
}
else
{
return \IPS\core\api\GraphQL\TypeRegistry::contentSearchResult();
return TypeRegistry::contentSearchResult();
}
}
];
@@ -12,23 +12,25 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\core\modules\front\search\search;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* SearchType for GraphQL API
*/
class _SearchType extends ObjectType
class SearchType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -64,15 +66,17 @@ class _SearchType extends ObjectType
{
case 'key':
return $type;
break;
case 'lang':
return \IPS\Member::loggedIn()->language()->get( $type . '_pl' );
break;
return Member::loggedIn()->language()->get( $type . '_pl' );
}
return null;
}
]) ),
'resolve' => function () {
return array_merge( array('core_members'), array_keys( \IPS\core\modules\front\search\search::contentTypes() ) );
return array_merge( array('core_members'), array_keys( search::contentTypes() ) );
}
]
];
@@ -10,25 +10,38 @@
*/
namespace IPS\core\api\GraphQL\Types;
use Exception;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\core\Reports\Report;
use IPS\core\Reports\Types;
use IPS\File;
use IPS\forums\Topic;
use IPS\GeoLocation;
use IPS\Helpers\Form\Editor;
use IPS\Helpers\Form\Upload;
use IPS\Login;
use IPS\Member;
use IPS\Settings;
use function defined;
use function strtoupper;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* SettingsType for GraphQL API
*/
class _SettingsType extends ObjectType
class SettingsType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -91,7 +104,7 @@ class _SettingsType extends ObjectType
'values' => ['NORMAL', 'FULL', 'REDIRECT', 'DISABLED']
]),
'resolve' => function ($val, $args, $context, $info) {
return \strtoupper( \IPS\Login::registrationType() );
return strtoupper( Login::registrationType() );
}
],
'allow_reg_target' => [
@@ -103,13 +116,13 @@ class _SettingsType extends ObjectType
'geolocation_enabled' => [
'type' => TypeRegistry::boolean(),
'resolve' => function () {
return \IPS\GeoLocation::enabled();
return GeoLocation::enabled();
}
],
'version' => [
'type' => TypeRegistry::int(),
'resolve' => function () {
return \IPS\Application::load('core')->long_version;
return Application::load('core')->long_version;
}
],
@@ -121,13 +134,13 @@ class _SettingsType extends ObjectType
'values' => ['INTERNAL', 'EXTERNAL', 'NONE']
]),
'resolve' => function ($val) {
return mb_strtoupper( \IPS\Settings::i()->privacy_type );
return mb_strtoupper( Settings::i()->privacy_type );
}
],
'privacy_text' => [
'type' => TypeRegistry::richText(),
'resolve' => function () {
return \IPS\Member::loggedIn()->language()->get('privacy_text_value');
return Member::loggedIn()->language()->get('privacy_text_value');
}
],
'privacy_link' => [
@@ -136,7 +149,7 @@ class _SettingsType extends ObjectType
'reg_rules' => [
'type' => TypeRegistry::richText(),
'resolve' => function () {
return \IPS\Member::loggedIn()->language()->get('reg_rules_value');
return Member::loggedIn()->language()->get('reg_rules_value');
}
],
'guidelines_type' => [
@@ -145,31 +158,28 @@ class _SettingsType extends ObjectType
'values' => ['INTERNAL', 'EXTERNAL', 'NONE']
]),
'resolve' => function ($val) {
return mb_strtoupper( \IPS\Settings::i()->gl_type );
return mb_strtoupper( Settings::i()->gl_type );
}
],
'guidelines_text' => [
'type' => TypeRegistry::richText(),
'resolve' => function ($val) {
return \IPS\Member::loggedIn()->language()->get('guidelines_value');
return Member::loggedIn()->language()->get('guidelines_value');
}
],
'guidelines_link' => [
'type' => TypeRegistry::string(),
'resolve' => function ($val) {
return \IPS\Settings::i()->gl_link;
return Settings::i()->gl_link;
}
],
/* -------------------------------- */
/* Forums settings */
'forums_questions_downvote' => [
'type' => TypeRegistry::boolean()
],
'forums_uses_solved' => [
'type' => TypeRegistry::boolean(),
'resolve' => function ($val) {
return \IPS\Application::appIsEnabled('forums') && \IPS\forums\Topic::anyContainerAllowsSolvable();
return Application::appIsEnabled('forums') && Topic::anyContainerAllowsSolvable();
}
],
@@ -179,14 +189,14 @@ class _SettingsType extends ObjectType
'type' => TypeRegistry::listOf( TypeRegistry::string() ),
'description' => "File extensions that are allowed to be uploaded or NULL for any extensions. Note: may be an empty array which means attachments not allowed.",
'resolve' => function() {
return \IPS\Helpers\Form\Editor::allowedFileExtensions();
return Editor::allowedFileExtensions();
}
],
'chunkingSupported' => [
'type' => TypeRegistry::boolean(),
'description' => "If chunking is supported",
'resolve' => function() {
$storageClass = \IPS\File::getClass( 'core_Attachment' );
$storageClass = File::getClass( 'core_Attachment' );
return $storageClass::$supportsChunking;
}
],
@@ -194,7 +204,7 @@ class _SettingsType extends ObjectType
'type' => TypeRegistry::int(),
'description' => "The maximum size (in bytes) the server can handle without crashing. If chunking is supported, you can send chunks of up to this size - if it isn't, this effectively becomes the maximum size per file.",
'resolve' => function() {
return \IPS\Helpers\Form\Upload::maxChunkSize();
return Upload::maxChunkSize();
}
],
@@ -204,7 +214,7 @@ class _SettingsType extends ObjectType
'type' => TypeRegistry::boolean(),
'description' => "Whether the automoderation feature is enabled",
'resolve' => function () {
return \IPS\Settings::i()->automoderation_enabled;
return Settings::i()->automoderation_enabled;
}
],
'reportReasons' => [
@@ -214,11 +224,11 @@ class _SettingsType extends ObjectType
$options = array();
$options[] = array(
'id' => \IPS\core\Reports\Report::TYPE_MESSAGE,
'reason' => \IPS\Member::loggedIn()->language()->addToStack('report_message_item')
'id' => Report::TYPE_MESSAGE,
'reason' => Member::loggedIn()->language()->addToStack('report_message_item')
);
foreach( \IPS\core\Reports\Types::roots() as $type )
foreach( Types::roots() as $type )
{
$options[] = array(
'id' => $type->id,
@@ -235,9 +245,9 @@ class _SettingsType extends ObjectType
$setting = $info->fieldName;
try
{
return \IPS\Settings::i()->$setting;
return Settings::i()->$setting;
}
catch(\Exception $error)
catch(Exception $error)
{
return null;
}
@@ -10,25 +10,32 @@
*/
namespace IPS\core\api\GraphQL\Types;
use DateInterval;
use Exception;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Application;
use IPS\Data\Cache;
use IPS\DateTime;
use IPS\Db;
use OutOfRangeException;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* StatsType for GraphQL API
*/
class _StatsType extends ObjectType
class StatsType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -41,7 +48,7 @@ class _StatsType extends ObjectType
'type' => TypeRegistry::int(),
'description' => "Content count",
'resolve' => function () {
return (int) static::getContentCount();
return static::getContentCount();
}
],
'memberCount' => [
@@ -58,18 +65,18 @@ class _StatsType extends ObjectType
parent::__construct($config);
}
protected static function getContentCount()
protected static function getContentCount() : int
{
$cacheKey = 'content_count';
$total = 0;
try
{
return \IPS\Data\Cache::i()->getWithExpire( $cacheKey, TRUE );
return Cache::i()->getWithExpire( $cacheKey, TRUE );
}
catch( \OutOfRangeException $e ){}
catch( OutOfRangeException $e ){}
foreach( \IPS\Application::enabledApplications() as $app )
foreach( Application::enabledApplications() as $app )
{
foreach( $app->extensions( 'core', 'ContentRouter', TRUE, TRUE ) as $object )
{
@@ -121,25 +128,24 @@ class _StatsType extends ObjectType
}
}
}
catch( \Exception $e ){}
catch( Exception $e ){}
}
}
}
}
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $total, \IPS\DateTime::create()->add( new \DateInterval('P1W') ), TRUE );
Cache::i()->storeWithExpire( $cacheKey, $total, DateTime::create()->add( new DateInterval('P1W') ), TRUE );
return $total;
}
protected static function getMemberCount()
protected static function getMemberCount() : ?int
{
try
{
$memberCount = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( 'completed=?', true ) )->first();
return $memberCount;
return Db::i()->select( 'COUNT(*)', 'core_members', array( 'completed=?', true ) )->first();
}
catch ( \Exception $e )
catch ( Exception $e )
{
return NULL;
}
@@ -12,23 +12,27 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content\Search\Query;
use IPS\Content\Search\Results;
use IPS\core\Stream;
use IPS\Member;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* StreamType for GraphQL API
*/
class _StreamType extends ObjectType
class StreamType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -49,7 +53,7 @@ class _StreamType extends ObjectType
'description' => "Stream title",
'resolve' => function ($stream) {
if( !$stream->title ) {
return \IPS\Member::loggedIn()->language()->addToStack( "stream_title_{$stream->id}" );
return Member::loggedIn()->language()->addToStack( "stream_title_{$stream->id}" );
}
return $stream->title;
}
@@ -58,7 +62,7 @@ class _StreamType extends ObjectType
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
'description' => "Stream owner, if applicable",
'resolve' => function ($stream) {
return ( $stream->member ) ? \IPS\Member::load( $stream->member ) : null;
return ( $stream->member ) ? Member::load( $stream->member ) : null;
}
],
'isDefault' => [
@@ -98,11 +102,12 @@ class _StreamType extends ObjectType
/**
* Resolve items
*
* @param \IPS\core\Stream
* @param array Arguments passed from resolver
* @return array
* @param Stream $stream
* @param array $args Arguments passed from resolver
* @param array $context
* @return Results
*/
protected static function items( $stream, $args, $context )
protected static function items( Stream $stream, array $args, array $context ) : Results
{
/* Clubs */
if( isset( $args['club'] ) )
@@ -120,7 +125,7 @@ class _StreamType extends ObjectType
}
/* Build the query */
$query = $stream->query( \IPS\Member::loggedIn() );
$query = $stream->query( Member::loggedIn() );
// Get page
// We don't know the count at this stage, so figure out the page number from
@@ -137,7 +142,7 @@ class _StreamType extends ObjectType
$query->setLimit( $limit )->setPage( $page );
/* Get the results */
$results = $query->search( NULL, NULL, ( $stream->include_comments ? \IPS\Content\Search\Query::TAGS_MATCH_ITEMS_ONLY + \IPS\Content\Search\Query::TERM_OR_TAGS : \IPS\Content\Search\Query::TERM_OR_TAGS ) );
$results = $query->search( NULL, NULL, ( $stream->include_comments ? Query::TAGS_MATCH_ITEMS_ONLY + Query::TERM_OR_TAGS : Query::TERM_OR_TAGS ) );
/* Load data we need like the authors, etc */
$results->init();
@@ -12,23 +12,24 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Http\Url;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* TagType for GraphQL API
*/
class _TagType extends ObjectType
class TagType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
@@ -49,7 +50,7 @@ class _TagType extends ObjectType
'description' => "Tag URL",
'resolve' => function ($tag) {
$_tag = urlencode($tag);
return \IPS\Http\Url::internal( "app=core&module=search&controller=search&tags={$_tag}", 'front', 'tags' );
return Url::internal( "app=core&module=discover&controller=tag&tag={$_tag}", 'front', 'tags' );
}
]
];
@@ -12,23 +12,23 @@
namespace IPS\core\api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use function defined;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* UploadProgressType for GraphQL API
*/
class _UploadProgressType extends ObjectType
class UploadProgressType extends ObjectType
{
/**
* Get object type
*
* @return ObjectType
*/
public function __construct()
{
+185 -154
View File
@@ -11,16 +11,36 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use BadMethodCallException;
use IPS\Api\Controller;
use IPS\Api\Exception;
use IPS\Api\PaginatedResponse;
use IPS\Api\Response;
use IPS\Application;
use IPS\core\extensions\nexus\Item\ClubMembership;
use IPS\Db;
use IPS\GeoLocation;
use IPS\Member;
use IPS\Member\Club;
use IPS\nexus\Customer;
use IPS\Request;
use IPS\Settings;
use IPS\Task;
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;
}
/**
* @brief Clubs API
*/
class _clubs extends \IPS\Api\Controller
class clubs extends Controller
{
/**
* GET /core/clubs
@@ -30,18 +50,19 @@ class _clubs extends \IPS\Api\Controller
* @apiparam int perPage Number of results per page - defaults to 25
* @apiparam int member_id Member ID to return only clubs the member is allowed to view.
* @note For requests using an OAuth Access Token for a particular member, only clubs the authorized user can view will be included and the member_id parameter will be ignored.
* @return \IPS\Api\PaginatedResponse<IPS\Member\Club>
* @apireturn PaginatedResponse<IPS\Member\Club>
* @return PaginatedResponse<Club>
*/
public function GETindex()
public function GETindex(): PaginatedResponse
{
$page = isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1;
$perPage = isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : 25;
$page = isset( Request::i()->page ) ? Request::i()->page : 1;
$perPage = isset( Request::i()->perPage ) ? Request::i()->perPage : 25;
$forMember = $this->member;
if( !$this->member AND isset( \IPS\Request::i()->member_id ) )
if( !$this->member AND isset( Request::i()->member_id ) )
{
$forMember = \IPS\Member::load( \IPS\Request::i()->member_id );
$forMember = Member::load( Request::i()->member_id );
if( !$forMember->member_id )
{
@@ -50,12 +71,12 @@ class _clubs extends \IPS\Api\Controller
}
/* Return */
return new \IPS\Api\PaginatedResponse(
return new PaginatedResponse(
200,
\IPS\Member\Club::clubs( $forMember, array( ( $page - 1 ) * $perPage, $perPage ), 'created' ),
Club::clubs( $forMember, array( ( $page - 1 ) * $perPage, $perPage ), 'created' ),
$page,
'IPS\Member\Club',
\IPS\Member\Club::clubs( $forMember, array( ( $page - 1 ) * $perPage, $perPage ), 'created', FALSE, array(), NULL, TRUE ),
Club::clubs( $forMember, array( ( $page - 1 ) * $perPage, $perPage ), 'created', FALSE, array(), NULL, TRUE ),
$this->member,
$perPage
);
@@ -67,23 +88,24 @@ class _clubs extends \IPS\Api\Controller
*
* @param int $id ID Number
* @throws 1C386/1 INVALID_ID The club does not exist or the authorized user does not have permission to view it
* @return \IPS\Member\Club
* @apireturn \IPS\Member\Club
* @return Response
*/
public function GETitem( $id )
public function GETitem( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if ( $this->member and !$club->canView( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
return new \IPS\Api\Response( 200, $club->apiOutput( $this->member ) );
return new Response( 200, $club->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/1', 404 );
throw new Exception( 'INVALID_ID', '1C386/1', 404 );
}
}
@@ -102,26 +124,27 @@ class _clubs extends \IPS\Api\Controller
* @apiparam string showMemberTab Who can see the list of members: nonmember = Everyone can see, member = Only members can see, moderator = Only moderators can see. Defaults to nonmember.
* @apiparam \IPS\nexus\Money joiningFee Cost to join the club (Nexus must be installed, paid clubs must be enabled, and the owner must be allowed to create paid clubs)
* @apiparam \IPS\nexus\Purchase\RenewalTerm renewalTerm Renewal term for the club (joiningFee must be set)
* @return \IPS\Member\Club
* @apireturn \IPS\Member\Club
* @note For requests using an OAuth Access Token for a particular member, the authorized user will be the club owner, otherwise you must pass an owner parameter with a valid member ID to set the club owner
* @throws 1C386/3 OWNER_REQUIRED An owner for the club is required. For requests NOT using an OAuth Access Token for a particular member, you must supply a member ID for the owner property
* @throws 1C386/4 NAME_REQUIRED A name is required for the club
* @throws 1C386/J CANNOT_CREATE The authorized member or supplied owner cannot create the type of club requested
* @throws 1C386/K CLUB_LIMIT_REACHED The authorized member or supplied owner has reached the maximum number of clubs they are allowed to create based on group restrictions
* @return Response
*/
public function POSTindex()
public function POSTindex(): Response
{
/* We need an owner */
if( !$this->member AND !\IPS\Request::i()->owner )
if( !$this->member AND !Request::i()->owner )
{
throw new \IPS\Api\Exception( 'OWNER_REQUIRED', '1C386/3', 400 );
throw new Exception( 'OWNER_REQUIRED', '1C386/3', 400 );
}
$owner = $this->member ?: \IPS\Member::load( \IPS\Request::i()->owner );
$owner = $this->member ?: Member::load( Request::i()->owner );
if( !\IPS\Request::i()->name )
if( !Request::i()->name )
{
throw new \IPS\Api\Exception( 'NAME_REQUIRED', '1C386/4', 400 );
throw new Exception( 'NAME_REQUIRED', '1C386/4', 400 );
}
$availableTypes = array();
@@ -135,40 +158,40 @@ class _clubs extends \IPS\Api\Controller
}
/* Default club type to 'open' if not specified */
if( !isset( \IPS\Request::i()->type ) OR !\IPS\Request::i()->type )
if( !isset( Request::i()->type ) OR !Request::i()->type )
{
\IPS\Request::i()->type = 'open';
Request::i()->type = 'open';
}
if ( !$availableTypes OR !\in_array( \IPS\Request::i()->type, array_keys( $availableTypes ) ) )
if ( !$availableTypes OR !in_array( Request::i()->type, array_keys( $availableTypes ) ) )
{
throw new \IPS\Api\Exception( 'CANNOT_CREATE', '1C386/J', 403 );
throw new Exception( 'CANNOT_CREATE', '1C386/J', 403 );
}
if ( $owner->group['g_club_limit'] )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', array( 'owner=?', $owner->member_id ) )->first() >= $owner->group['g_club_limit'] )
if ( Db::i()->select( 'COUNT(*)', 'core_clubs', array( 'owner=?', $owner->member_id ) )->first() >= $owner->group['g_club_limit'] )
{
throw new \IPS\Api\Exception( 'CLUB_LIMIT_REACHED', '1C386/K', 403 );
throw new Exception( 'CLUB_LIMIT_REACHED', '1C386/K', 403 );
}
}
$club = new \IPS\Member\Club;
$club = new Club;
$club->owner = $owner;
$this->setClubProperties( $club );
$club->save();
$club->addMember( $owner, \IPS\Member\Club::STATUS_LEADER );
$club->addMember( $owner, Club::STATUS_LEADER );
$club->recountMembers();
if( \IPS\Settings::i()->clubs_require_approval and !$club->approved )
if( Settings::i()->clubs_require_approval and !$club->approved )
{
$club->sendModeratorApprovalNotification( $owner );
}
return new \IPS\Api\Response( 201, $club->apiOutput( $this->member ) );
return new Response( 201, $club->apiOutput( $this->member ) );
}
/**
@@ -186,28 +209,29 @@ class _clubs extends \IPS\Api\Controller
* @apiparam \IPS\nexus\Money joiningFee Cost to join the club (Nexus must be installed, paid clubs must be enabled, and the owner must be allowed to create paid clubs)
* @apiparam \IPS\nexus\Purchase\RenewalTerm renewalTerm Renewal term for the club (joiningFee must be set)
* @param int $id ID Number
* @return \IPS\Member\Club
* @apireturn \IPS\Member\Club
* @throws 1C386/5 INVALID_ID The club ID was invalid or the authorized member does not have permission to edit it
* @throws 1C386/L CANNOT_CREATE The authorized member or supplied owner cannot create the type of club requested
* @return Response
*/
public function POSTitem( $id )
public function POSTitem( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND !$club->isLeader( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/5', 404 );
throw new Exception( 'INVALID_ID', '1C386/5', 404 );
}
/* Make sure the club type is allowed */
if( isset( \IPS\Request::i()->type ) )
if( isset( Request::i()->type ) )
{
$owner = $this->member ?: $club->owner;
@@ -221,9 +245,9 @@ class _clubs extends \IPS\Api\Controller
}
}
if ( !$availableTypes OR !\in_array( \IPS\Request::i()->type, array_keys( $availableTypes ) ) )
if ( !$availableTypes OR !in_array( Request::i()->type, array_keys( $availableTypes ) ) )
{
throw new \IPS\Api\Exception( 'CANNOT_CREATE', '1C386/L', 403 );
throw new Exception( 'CANNOT_CREATE', '1C386/L', 403 );
}
}
@@ -231,61 +255,61 @@ class _clubs extends \IPS\Api\Controller
$club->save();
return new \IPS\Api\Response( 200, $club->apiOutput( $this->member ) );
return new Response( 200, $club->apiOutput( $this->member ) );
}
/**
* Set common club properties
*
* @param \IPS\Member\Club $club The club object to set the properties on
* @param Club $club The club object to set the properties on
* @return void
*/
protected function setClubProperties( $club )
protected function setClubProperties( Club $club ) : void
{
if( \IPS\Request::i()->name )
if( Request::i()->name )
{
$club->name = \IPS\Request::i()->name;
$club->name = Request::i()->name;
}
if( isset( \IPS\Request::i()->about ) )
if( isset( Request::i()->about ) )
{
$club->about = \IPS\Request::i()->about;
$club->about = Request::i()->about;
}
if( isset( \IPS\Request::i()->showMemberTab ) AND \in_array( \IPS\Request::i()->showMemberTab, array( 'member', 'nonmember', 'moderator' ) ) )
if( isset( Request::i()->showMemberTab ) AND in_array( Request::i()->showMemberTab, array( 'member', 'nonmember', 'moderator' ) ) )
{
$club->show_membertab = \IPS\Request::i()->showMemberTab;
$club->show_membertab = Request::i()->showMemberTab;
}
else
{
$club->show_membertab = 'nonmember';
}
if( isset( \IPS\Request::i()->type ) AND \in_array( \IPS\Request::i()->type, array( 'open', 'public', 'readonly', 'closed', 'private' ) ) )
if( isset( Request::i()->type ) AND in_array( Request::i()->type, array( 'open', 'public', 'readonly', 'closed', 'private' ) ) )
{
$club->type = \IPS\Request::i()->type;
$club->type = Request::i()->type;
}
if( !$this->member AND isset( \IPS\Request::i()->approved ) )
if( !$this->member AND isset( Request::i()->approved ) )
{
$club->approved = \IPS\Request::i()->approved;
$club->approved = Request::i()->approved;
}
/* Set club approval based on AdminCP settings, but only if this is a new club */
elseif( !$club->id )
{
$club->approved = \IPS\Settings::i()->clubs_require_approval ? 0 : 1;
$club->approved = Settings::i()->clubs_require_approval ? 0 : 1;
}
if( !$this->member AND isset( \IPS\Request::i()->featured ) )
if( !$this->member AND isset( Request::i()->featured ) )
{
$club->featured = \IPS\Request::i()->featured;
$club->featured = Request::i()->featured;
}
if( \IPS\Settings::i()->clubs_locations AND isset( \IPS\Request::i()->lat ) AND isset( \IPS\Request::i()->long ) )
if( Settings::i()->clubs_locations AND isset( Request::i()->lat ) AND isset( Request::i()->long ) )
{
try
{
$location = \IPS\GeoLocation::getByLatLong( \IPS\Request::i()->lat, \IPS\Request::i()->long );
$location = GeoLocation::getByLatLong( Request::i()->lat, Request::i()->long );
$club->location_json = json_encode( $location );
$club->location_lat = $location->lat;
@@ -294,17 +318,17 @@ class _clubs extends \IPS\Api\Controller
catch( \Exception $e ){}
}
if( isset( \IPS\Request::i()->joiningFee ) AND \IPS\Request::i()->joiningFee )
if( isset( Request::i()->joiningFee ) AND Request::i()->joiningFee )
{
if ( $club->owner->member_id AND \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on and $club->owner->group['gbw_paid_clubs'] )
if ( $club->owner->member_id AND Application::appIsEnabled( 'nexus' ) and Settings::i()->clubs_paid_on and $club->owner->group['gbw_paid_clubs'] )
{
$club->fee = json_encode( \IPS\Request::i()->joiningFee );
$club->fee = json_encode( Request::i()->joiningFee );
if ( isset( \IPS\Request::i()->renewalTerm ) AND \IPS\Request::i()->renewalTerm )
if ( isset( Request::i()->renewalTerm ) AND Request::i()->renewalTerm )
{
$club->renewal_term = \IPS\Request::i()->renewalTerm['term'];
$club->renewal_units = \IPS\Request::i()->renewalTerm['unit'];
$club->renewal_price = json_encode( \IPS\Request::i()->renewalTerm['cost'] );
$club->renewal_term = Request::i()->renewalTerm['term'];
$club->renewal_units = Request::i()->renewalTerm['unit'];
$club->renewal_price = json_encode( Request::i()->renewalTerm['cost'] );
}
else
{
@@ -322,24 +346,25 @@ class _clubs extends \IPS\Api\Controller
*
* @apiclientonly
* @param int $id ID Number
* @return null
* @apireturn null
* @throws 1C386/2 INVALID_ID The club does not exist
* @return Response
*/
public function DELETEitem( $id )
public function DELETEitem( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
/* Deletions cannot be performed by a regular user */
if( $this->member )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch ( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/2', 404 );
throw new Exception( 'INVALID_ID', '1C386/2', 404 );
}
/* Get nodes and queue for deletion */
@@ -364,7 +389,7 @@ class _clubs extends \IPS\Api\Controller
foreach ( $nodesToQueue as $_node )
{
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => $class, 'id' => $_node->_id, 'deleteWhenDone' => TRUE, 'additional' => array() ) );
Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => $class, 'id' => $_node->_id, 'deleteWhenDone' => TRUE, 'additional' => array() ) );
}
}
catch( \Exception $e ){}
@@ -372,11 +397,11 @@ class _clubs extends \IPS\Api\Controller
/* Now delete the club and associated data */
$club->delete();
\IPS\Db::i()->delete( 'core_clubs_memberships', array( 'club_id=?', $club->id ) );
\IPS\Db::i()->delete( 'core_clubs_node_map', array( 'club_id=?', $club->id ) );
\IPS\Db::i()->delete( 'core_clubs_fieldvalues', array( 'club_id=?', $club->id ) );
Db::i()->delete( 'core_clubs_memberships', array( 'club_id=?', $club->id ) );
Db::i()->delete( 'core_clubs_node_map', array( 'club_id=?', $club->id ) );
Db::i()->delete( 'core_clubs_fieldvalues', array( 'club_id=?', $club->id ) );
return new \IPS\Api\Response( 200, NULL );
return new Response( 200, NULL );
}
/**
@@ -390,21 +415,22 @@ class _clubs extends \IPS\Api\Controller
* @apiresponse [\IPS\Member] members Club members
* @apiresponse [\IPS\Member] leaders Club leaders
* @apiresponse [\IPS\Member] moderators Club moderators
* @return array
* @apireturn array
* @return Response
*/
public function GETitem_members( $id )
public function GETitem_members( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND !$club->canView( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
elseif( $club->type === \IPS\Member\Club::TYPE_PUBLIC )
elseif( $club->type === Club::TYPE_PUBLIC )
{
throw new \BadMethodCallException;
throw new BadMethodCallException;
}
$members = array();
@@ -413,7 +439,7 @@ class _clubs extends \IPS\Api\Controller
foreach( $club->members( array( 'member', 'moderator', 'leader' ), 250, 'core_clubs_memberships.joined DESC', 2 ) as $member )
{
$member = \IPS\Member::constructFromData( $member );
$member = Member::constructFromData( $member );
if( $club->owner != $member )
{
@@ -432,15 +458,15 @@ class _clubs extends \IPS\Api\Controller
}
}
return new \IPS\Api\Response( 200, array( 'owner' => $club->owner ? $club->owner->apiOutput() : NULL, 'members' => $members, 'leaders' => $leaders, 'moderators' => $moderators ) );
return new Response( 200, array( 'owner' => $club->owner ? $club->owner->apiOutput() : NULL, 'members' => $members, 'leaders' => $leaders, 'moderators' => $moderators ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/6', 404 );
throw new Exception( 'INVALID_ID', '1C386/6', 404 );
}
catch( \BadMethodCallException $e )
catch( BadMethodCallException $e )
{
throw new \IPS\Api\Exception( 'NO_MEMBERS_PUBLIC_CLUB', '1C386/I', 400 );
throw new Exception( 'NO_MEMBERS_PUBLIC_CLUB', '1C386/I', 400 );
}
}
@@ -454,33 +480,34 @@ class _clubs extends \IPS\Api\Controller
* @apiparam int waiveFee If set to 1 and the request is made by a club leader, the join fee will be waived for the member being invited
* @throws 1C386/7 INVALID_ID The club does not exist or the authorized user does not have permission to add members to it
* @throws 1C386/8 INVALID_MEMBER The member to be added could not be found or the member cannot join the club
* @return array
* @apireturn array
* @apiresponse \IPS\Member owner Club owner
* @apiresponse [\IPS\Member] members Club members
* @apiresponse [\IPS\Member] leaders Club leaders
* @apiresponse [\IPS\Member] moderators Club moderators
* @note If the member already exists they will be updated. This can be used to ban a member from a club or promote a member to a leader, for instance.
* @note If using an API key, the id parameter is required and will indicate the member being added to the club. If using an OAuth access token and the request is made by a club leader, the id parameter is required. If the user is already a member of the club they can be moved to a different status (such as banned or moderator), and if the user is not a member of the club, they will be invited (and the waiveFee parameter can be passed to bypass any club joining fee). If using an OAuth access token and no id is passed, a request to join will be submitted if necessary, or they will be added to the club (the status parameter is ignored). Finally, if using an OAuth access token and an id parameter is provided, an invitation will be sent to that user.
* @return Response
*/
public function POSTitem_members( $id )
public function POSTitem_members( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND isset( \IPS\Request::i()->id ) )
if( $this->member AND isset( Request::i()->id ) )
{
$member = \IPS\Member::load( \IPS\Request::i()->id );
$member = Member::load( Request::i()->id );
}
else
{
$member = $this->member ?: \IPS\Member::load( \IPS\Request::i()->id );
$member = $this->member ?: Member::load( Request::i()->id );
}
/* Do we have the member? */
if( !$member OR !$member->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_MEMBER', '1C386/8', 404 );
throw new Exception( 'INVALID_MEMBER', '1C386/8', 404 );
}
$currentStatus = $club->memberStatus( $member );
@@ -488,65 +515,65 @@ class _clubs extends \IPS\Api\Controller
/* If this is an API key request, just do it */
if( !$this->member )
{
$newStatus = \IPS\Request::i()->status ?: \IPS\Member\Club::STATUS_MEMBER;
$newStatus = Request::i()->status ?: Club::STATUS_MEMBER;
if( !\in_array( $newStatus, array( \IPS\Member\Club::STATUS_MEMBER, \IPS\Member\Club::STATUS_INVITED, \IPS\Member\Club::STATUS_REQUESTED, \IPS\Member\Club::STATUS_INVITED_BYPASSING_PAYMENT, \IPS\Member\Club::STATUS_WAITING_PAYMENT, \IPS\Member\Club::STATUS_EXPIRED, \IPS\Member\Club::STATUS_EXPIRED_MODERATOR, \IPS\Member\Club::STATUS_DECLINED, \IPS\Member\Club::STATUS_BANNED, \IPS\Member\Club::STATUS_MODERATOR, \IPS\Member\Club::STATUS_LEADER ) ) )
if( !in_array( $newStatus, array( Club::STATUS_MEMBER, Club::STATUS_INVITED, Club::STATUS_REQUESTED, Club::STATUS_INVITED_BYPASSING_PAYMENT, Club::STATUS_WAITING_PAYMENT, Club::STATUS_EXPIRED, Club::STATUS_EXPIRED_MODERATOR, Club::STATUS_DECLINED, Club::STATUS_BANNED, Club::STATUS_MODERATOR, Club::STATUS_LEADER ) ) )
{
$newStatus = \IPS\Member\Club::STATUS_MEMBER;
$newStatus = Club::STATUS_MEMBER;
}
$club->addMember( $member, $newStatus, TRUE );
}
/* If this is an OAuth member request for their _own_ account, we need to check joining fees, etc. */
elseif( $this->member AND $member == $this->member )
elseif( $member === $this->member )
{
if( !$club->canJoin( $member ) )
{
throw new \IPS\Api\Exception( 'CANNOT_JOIN', '1C386/M', 403 );
throw new Exception( 'CANNOT_JOIN', '1C386/M', 403 );
}
/* If this is an open club, or the member was invited, or they have mod access anyway go ahead and add them */
if ( \in_array( $currentStatus, array( \IPS\Member\Club::STATUS_INVITED, \IPS\Member\Club::STATUS_INVITED_BYPASSING_PAYMENT, \IPS\Member\Club::STATUS_WAITING_PAYMENT ) ) or $club->type === \IPS\Member\Club::TYPE_OPEN or $member->modPermission('can_access_all_clubs') )
if ( in_array( $currentStatus, array( Club::STATUS_INVITED, Club::STATUS_INVITED_BYPASSING_PAYMENT, Club::STATUS_WAITING_PAYMENT ) ) or $club->type === Club::TYPE_OPEN or $member->modPermission('can_access_all_clubs') )
{
/* Unless they have to pay */
if ( $club->isPaid() and $currentStatus !== \IPS\Member\Club::STATUS_INVITED_BYPASSING_PAYMENT )
if ( $club->isPaid() and $currentStatus !== Club::STATUS_INVITED_BYPASSING_PAYMENT )
{
if ( $club->joiningFee() )
{
$club->generateInvoice( \IPS\nexus\Customer::load( $member->member_id ) );
$club->generateInvoice( Customer::load( $member->member_id ) );
}
}
else
{
$club->addMember( $member, \IPS\Member\Club::STATUS_MEMBER, TRUE, NULL, NULL, TRUE );
$club->addMember( $member, Club::STATUS_MEMBER, TRUE, NULL, NULL, TRUE );
$club->recountMembers();
}
}
/* Otherwise, add the request */
else
{
$club->addMember( $member, \IPS\Member\Club::STATUS_REQUESTED, TRUE, ( $this->member AND $this->member != $member ) ? $this->member : NULL );
$club->addMember( $member, Club::STATUS_REQUESTED, TRUE );
}
}
/* If this is an OAuth request for someone else's account and we are not a leader, then we should send an invite */
elseif( $this->member AND $member != $this->member AND !$club->isLeader( $this->member ) )
elseif( !$club->isLeader( $this->member ) )
{
if ( !$club->canInvite( $this->member ) )
{
throw new \IPS\Api\Exception( 'CANNOT_INVITE', '1C386/N', 403 );
throw new Exception( 'CANNOT_INVITE', '1C386/N', 403 );
}
$club->addMember( $member, $club::STATUS_INVITED, TRUE, $member, $this->member, TRUE );
$club->sendInvitation( $this->member, array( $member ) );
}
/* And finally, if this is an OAuth request for someone else's account and we are the leader, we should either send an invite or promote the member if the status is mod/leader */
elseif( $this->member AND $member != $this->member AND $club->isLeader( $this->member ) )
elseif( $club->isLeader( $this->member ) )
{
/* If the member is not currently a part of the club, send an invite */
if( $currentStatus === NULL )
{
$status = $club::STATUS_INVITED;
if ( $club->isPaid() and \IPS\Request::i()->waiveFee )
if ( $club->isPaid() and Request::i()->waiveFee )
{
$status = $club::STATUS_INVITED_BYPASSING_PAYMENT;
}
@@ -554,13 +581,13 @@ class _clubs extends \IPS\Api\Controller
$club->addMember( $member, $status, TRUE, $this->member, $this->member, TRUE );
$club->sendInvitation( $this->member, array( $member ) );
}
elseif( !\in_array( \IPS\Request::i()->status, array( \IPS\Member\Club::STATUS_REQUESTED, \IPS\Member\Club::STATUS_INVITED, \IPS\Member\Club::STATUS_INVITED_BYPASSING_PAYMENT ) ) )
elseif( !in_array( Request::i()->status, array( Club::STATUS_REQUESTED, Club::STATUS_INVITED, Club::STATUS_INVITED_BYPASSING_PAYMENT ) ) )
{
$club->addMember( $member, \IPS\Request::i()->status, TRUE, $this->member, $this->member, TRUE );
$club->addMember( $member, Request::i()->status, TRUE, $this->member, $this->member, TRUE );
}
else
{
throw new \IPS\Api\Exception( 'CANNOT_INVITE', '1C386/O', 403 );
throw new Exception( 'CANNOT_INVITE', '1C386/O', 403 );
}
}
@@ -568,9 +595,9 @@ class _clubs extends \IPS\Api\Controller
return $this->GETitem_members( $id );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/7', 404 );
throw new Exception( 'INVALID_ID', '1C386/7', 404 );
}
}
@@ -586,33 +613,34 @@ class _clubs extends \IPS\Api\Controller
* @apiresponse [\IPS\Member] members Club members
* @apiresponse [\IPS\Member] leaders Club leaders
* @apiresponse [\IPS\Member] moderators Club moderators
* @return array
* @apireturn array
* @return Response
*/
public function DELETEitem_members( $id, $memberId = 0 )
public function DELETEitem_members( int $id, int $memberId = 0 ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND !$club->isLeader( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
$member = \IPS\Member::load( $memberId );
$member = Member::load( $memberId );
if( !$member->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_MEMBER', '1C386/9', 404 );
throw new Exception( 'INVALID_MEMBER', '1C386/9', 404 );
}
$club->removeMember( $member );
$club->recountMembers();
/* Cancel purchase */
if ( \IPS\Application::appIsEnabled('nexus') and \IPS\Settings::i()->clubs_paid_on )
if ( Application::appIsEnabled('nexus') and Settings::i()->clubs_paid_on )
{
foreach ( \IPS\core\extensions\nexus\Item\ClubMembership::getPurchases( \IPS\nexus\Customer::load( $member->member_id ), $club->id ) as $purchase )
foreach ( ClubMembership::getPurchases( Customer::load( $member->member_id ), $club->id ) as $purchase )
{
$purchase->cancelled = TRUE;
$purchase->member->log( 'purchase', array( 'type' => 'cancel', 'id' => $purchase->id, 'name' => $purchase->name, 'by' => 'api' ) );
@@ -623,9 +651,9 @@ class _clubs extends \IPS\Api\Controller
return $this->GETitem_members( $id );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/A', 404 );
throw new Exception( 'INVALID_ID', '1C386/A', 404 );
}
}
@@ -636,13 +664,14 @@ class _clubs extends \IPS\Api\Controller
* @apiparam int memberId Restrict the returned types to only those that can be created by the supplied member ID
* @note For requests using an OAuth Access Token for a particular member, the memberId parameter is ignored and the authorized member is checked
* @apiresponse array contentTypes Available content types
* @return array
* @apireturn array
* @return Response
*/
public function GETcontenttypes()
public function GETcontenttypes(): Response
{
$member = $this->member ?: ( ( isset( \IPS\Request::i()->memberId ) ) ? \IPS\Member::load( \IPS\Request::i()->memberId ) : NULL );
$member = $this->member ?: ( ( isset( Request::i()->memberId ) ) ? Member::load( Request::i()->memberId ) : NULL );
return new \IPS\Api\Response( 200, array( 'contentTypes' => \IPS\Member\Club::availableNodeTypes( $member ) ) );
return new Response( 200, array( 'contentTypes' => Club::availableNodeTypes( $member ) ) );
}
/**
@@ -652,17 +681,18 @@ class _clubs extends \IPS\Api\Controller
* @param int $id ID Number
* @throws 1C386/6 INVALID_ID The club does not exist or the authorized user does not have permission to view it
* @apiresponse [\IPS\Node\Model] nodes Club nodes
* @return array
* @apireturn array
* @return Response
*/
public function GETitem_nodes( $id )
public function GETitem_nodes( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND !$club->canView( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
/* Format in a useful manner */
@@ -679,11 +709,11 @@ class _clubs extends \IPS\Api\Controller
}
}
return new \IPS\Api\Response( 200, array( 'nodes' => $nodes ) );
return new Response( 200, array( 'nodes' => $nodes ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/B', 404 );
throw new Exception( 'INVALID_ID', '1C386/B', 404 );
}
}
@@ -696,33 +726,34 @@ class _clubs extends \IPS\Api\Controller
* @reqapiparam string class Node (class) to remove
* @throws 1C386/H INVALID_ID The club does not exist or the current authorized member is not a leader of the club
* @throws 1C386/G INVALID_NODE The node to be deleted could not be found or the authorized user does not have permission to delete it
* @return null
* @apireturn void
* @return Response
*/
public function DELETEitem_nodes( $id )
public function DELETEitem_nodes( int $id ): Response
{
try
{
$club = \IPS\Member\Club::load( $id );
$club = Club::load( $id );
if( $this->member AND !$club->isLeader( $this->member ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
try
{
$class = \IPS\Request::i()->class;
$class = Request::i()->class;
if( !is_subclass_of( $class, "\IPS\Node\Model" ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
$node = $class::load( (int) \IPS\Request::i()->id );
$node = $class::load( (int) Request::i()->id );
if ( !$node->club() or $node->club()->id !== $club->id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
/* Permission check */
@@ -731,23 +762,23 @@ class _clubs extends \IPS\Api\Controller
$itemClass = $node::$contentItemClass;
if ( !$node->modPermission( 'delete', $this->member ) and $itemClass::contentCount( $node, TRUE, TRUE, TRUE, 1 ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_NODE', '1C386/G', 404 );
throw new Exception( 'INVALID_NODE', '1C386/G', 404 );
}
\IPS\Db::i()->delete( 'core_clubs_node_map', array( 'club_id=? AND node_class=? AND node_id=?', $club->id, $class, $node->_id ) );
Db::i()->delete( 'core_clubs_node_map', array( 'club_id=? AND node_class=? AND node_id=?', $club->id, $class, $node->_id ) );
$node->deleteOrMoveFormSubmit( array() );
return new \IPS\Api\Response( 200, NULL );
return new Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C386/H', 404 );
throw new Exception( 'INVALID_ID', '1C386/H', 404 );
}
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* @brief Content Item API
* @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 26 Aug 2023
*/
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Api\Exception;
use IPS\Api\Response;
use IPS\Application;
use IPS\Node\Api\NodeController;
use IPS\Request;
use OutOfRangeException;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* @brief Content Item API
*/
class content extends NodeController
{
/**
* Class
*/
protected string $class = '';
/**
* GET /core/content
* Get content item by class and ID
*
* @apiclientonly
* @param int $id ID Number
* @apiparam string class Class of content item to fetch e.g. IPS\forums\Topic
* @apiparam int id ID of content item to fetch
* @throws 1C436/1 INVALID_CLASS The class does not exist
* @throws 1C436/2 INVALID_ID The item does not exist
* @apireturn \IPS\Content\Item
* @return Response
*/
public function GETitem( int $id ): Response
{
foreach ( Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
{
foreach ( $contentRouter->classes as $class )
{
if( $class == Request::i()->class )
{
$this->class = $class;
}
}
}
if( !$this->class )
{
throw new Exception( 'INVALID_CLASS', '1C436/1', 404 );
}
try
{
return $this->_view( $id );
}
catch ( OutOfRangeException $e )
{
throw new Exception( 'INVALID_ID', '1C436/2', 404 );
}
}
}
+39 -25
View File
@@ -11,16 +11,27 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Controller;
use IPS\Api\Exception;
use IPS\Api\PaginatedResponse;
use IPS\Api\Response;
use IPS\Db;
use IPS\Member\Group;
use IPS\Request;
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;
}
/**
* @brief Member Groups API
*/
class _groups extends \IPS\Api\Controller
class groups extends Controller
{
/**
* GET /core/groups
@@ -28,22 +39,23 @@ class _groups extends \IPS\Api\Controller
*
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\Member\Group>
* @apireturn PaginatedResponse<IPS\Member\Group>
* @return PaginatedResponse<Group>
*/
public function GETindex()
public function GETindex(): PaginatedResponse
{
/* Where clause */
$where = array();
/* Return */
return new \IPS\Api\PaginatedResponse(
return new PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_groups', $where, "g_id asc" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
Db::i()->select( '*', 'core_groups', $where, "g_id asc" ),
isset( Request::i()->page ) ? Request::i()->page : 1,
'IPS\Member\Group',
\IPS\Db::i()->select( 'COUNT(*)', 'core_groups', $where )->first(),
Db::i()->select( 'COUNT(*)', 'core_groups', $where )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
isset( Request::i()->perPage ) ? Request::i()->perPage : NULL
);
}
@@ -53,23 +65,24 @@ class _groups extends \IPS\Api\Controller
*
* @param int $id ID Number
* @throws 1C358/1 INVALID_ID The group ID does not exist
* @return \IPS\Member\Group
* @apireturn \IPS\Member\Group
* @return Response
*/
public function GETitem( $id )
public function GETitem( int $id ): Response
{
try
{
$group = \IPS\Member\Group::load( $id );
$group = Group::load( $id );
if ( !$group->g_id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
return new \IPS\Api\Response( 200, $group->apiOutput( $this->member ) );
return new Response( 200, $group->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C358/1', 404 );
throw new Exception( 'INVALID_ID', '1C358/1', 404 );
}
}
@@ -81,30 +94,31 @@ class _groups extends \IPS\Api\Controller
* @param int $id ID Number
* @throws 2C358/3 CANNOT_DELETE The group can't be deleted
* @throws 1C358/2 INVALID_ID The group ID does not exist
* @return void
* @apireturn void
* @return Response
*/
public function DELETEitem( $id )
public function DELETEitem( int $id ): Response
{
try
{
$group = \IPS\Member\Group::load( $id );
$group = Group::load( $id );
if ( !$group->g_id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
if( !$group->canDelete() )
{
throw new \IPS\Api\Exception( 'CANNOT_DELETE', '2C358/3', 403 );
throw new Exception( 'CANNOT_DELETE', '2C358/3', 403 );
}
$group->delete();
return new \IPS\Api\Response( 200, NULL );
return new Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C358/2', 404 );
throw new Exception( 'INVALID_ID', '1C358/2', 404 );
}
}
}
+20 -10
View File
@@ -11,34 +11,44 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Controller;
use IPS\Api\Response;
use IPS\Application;
use IPS\IPS;
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;
}
/**
* @brief Hello API
*/
class _hello extends \IPS\Api\Controller
class hello extends Controller
{
/**
* GET /core/hello
* Get basic information about the community.
*
* @return array
* @apireturn array
* @apiresponse string communityName The name of the community
* @apiresponse string communityUrl The community URL
* @apiresponse string ipsVersion The Invision Community version number
* @apiresponse array applications The installed IPS Applications
* @return Response
*/
public function GETindex()
public function GETindex(): Response
{
return new \IPS\Api\Response( 200, array(
'communityName' => \IPS\Settings::i()->board_name,
'communityUrl' => \IPS\Settings::i()->base_url,
'ipsVersion' => \IPS\Application::load('core')->version,
'ipsApplications' => array_filter( array_keys( \IPS\Application::applications() ), function( $k ) { return \in_array( $k, \IPS\IPS::$ipsApps ); } )
return new Response( 200, array(
'communityName' => Settings::i()->board_name,
'communityUrl' => Settings::i()->base_url,
'ipsVersion' => Application::load('core')->version,
'ipsApplications' => array_filter( array_keys( Application::applications() ), function( $k ) { return in_array( $k, IPS::$ipsApps ); } )
) );
}
}
+19 -11
View File
@@ -12,25 +12,32 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Controller;
use IPS\Api\Response;
use IPS\DateTime;
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;
}
/**
* @brief Me API
*/
class _me extends \IPS\Api\Controller
class me extends Controller
{
/**
* GET /core/me
* Get basic information about the authorized user
*
* @apimemberonly
* @return \IPS\Member
* @apireturn \IPS\Member
* @return Response
*/
public function GETindex()
public function GETindex(): Response
{
$output = $this->member->apiOutput( $this->member );
@@ -39,10 +46,10 @@ class _me extends \IPS\Api\Controller
$output['email'] = $this->member->email;
}
$output['lastVisit'] = $this->member->last_visit ? \IPS\DateTime::ts( $this->member->last_visit )->rfc3339() : NULL;
$output['lastPost'] = $this->member->member_last_post ? \IPS\DateTime::ts( $this->member->member_last_post )->rfc3339() : NULL;
$output['lastVisit'] = $this->member->last_visit ? DateTime::ts( $this->member->last_visit )->rfc3339() : NULL;
$output['lastPost'] = $this->member->member_last_post ? DateTime::ts( $this->member->member_last_post )->rfc3339() : NULL;
return new \IPS\Api\Response( 200, $output );
return new Response( 200, $output );
}
/**
@@ -51,11 +58,12 @@ class _me extends \IPS\Api\Controller
*
* @apimemberonly
* @param string $path Requested path
* @return array
* @apireturn array
* @apiresponse string email Email address
* @return Response
*/
public function GETitem( $path )
public function GETitem( string $path = '' ): Response
{
return new \IPS\Api\Response( 200, array( 'email' => $this->member->email ) );
return new Response( 200, array( 'email' => $this->member->email ) );
}
}
File diff suppressed because it is too large. Load diff
+58 -39
View File
@@ -12,16 +12,31 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Controller;
use IPS\Api\Exception;
use IPS\Api\Response;
use IPS\core\Messenger\Conversation;
use IPS\core\Messenger\Message;
use IPS\DateTime;
use IPS\Member;
use IPS\Request;
use IPS\Text\Parser;
use OutOfRangeException;
use function count;
use function defined;
use function is_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;
}
/**
* @brief Personal Conversations API
*/
class _messages extends \IPS\Api\Controller
class messages extends Controller
{
/**
* POST /core/messages
@@ -36,60 +51,62 @@ class _messages extends \IPS\Api\Controller
* @throws 1C374/3 INVALID_RECIPIENT No recipients were supplied
* @throws 1C374/4 INVALID_RECIPIENT One or more recipients are invalid
* @throws 1C374/5 MISSING_TITLE_OR_BODY The title and/or body of the conversation were not supplied
* @return int Conversation ID
* @apireturn int Conversation ID
* @return Response
*/
public function POSTindex()
public function POSTindex(): Response
{
/* Make sure there is a valid sender */
if ( !isset( \IPS\Request::i()->from ) OR !\IPS\Member::load( (int) \IPS\Request::i()->from )->member_id )
if ( !isset( Request::i()->from ) OR !Member::load( (int) Request::i()->from )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_SENDER', '1C374/2', 404 );
throw new Exception( 'INVALID_SENDER', '1C374/2', 404 );
}
/* Verify there are recipients and all the recipients are valid */
if( !isset( \IPS\Request::i()->to ) OR !\is_array( \IPS\Request::i()->to ) OR !\count( \IPS\Request::i()->to ) )
if( !isset( Request::i()->to ) OR !is_array( Request::i()->to ) OR !count( Request::i()->to ) )
{
throw new \IPS\Api\Exception( 'INVALID_RECIPIENT', '1C374/3', 404 );
throw new Exception( 'INVALID_RECIPIENT', '1C374/3', 404 );
}
else
{
foreach( \IPS\Request::i()->to as $to )
foreach( Request::i()->to as $to )
{
if( !\IPS\Member::load( (int) $to )->member_id )
if( !Member::load( (int) $to )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_RECIPIENT', '1C374/4', 404 );
throw new Exception( 'INVALID_RECIPIENT', '1C374/4', 404 );
}
}
}
/* Make sure we have a title and body */
if( !isset( \IPS\Request::i()->title ) OR !isset( \IPS\Request::i()->body ) )
if( !isset( Request::i()->title ) OR !isset( Request::i()->body ) )
{
throw new \IPS\Api\Exception( 'MISSING_TITLE_OR_BODY', '1C374/5', 404 );
throw new Exception( 'MISSING_TITLE_OR_BODY', '1C374/5', 404 );
}
/* Create the conversation */
$item = \IPS\core\Messenger\Conversation::createItem( \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\Request::i()->ipAddress(), \IPS\DateTime::create(), NULL );
$item->title = \IPS\Request::i()->title;
$item->to_count = \count( \IPS\Request::i()->to );
$item = Conversation::createItem( Member::load( (int) Request::i()->from ), Request::i()->ipAddress(), DateTime::create() );
$item->title = Request::i()->title;
$item->to_count = count( Request::i()->to );
$item->save();
/* Create the first message */
$postContents = \IPS\Text\Parser::parseStatic( \IPS\Request::i()->body, TRUE, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), 'core_Messaging' );
$postContents = Parser::parseStatic( Request::i()->body, NULL, Member::load( (int)Request::i()->from ), 'core_Messaging' );
/** @var Message $commentClass */
$commentClass = $item::$commentClass;
$post = $commentClass::create( $item, $postContents, TRUE, NULL, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\DateTime::create() );
$post = $commentClass::create( $item, $postContents, TRUE, NULL, NULL, Member::load( (int) Request::i()->from ), DateTime::create() );
$item->first_msg_id = $post->id;
$item->save();
/* Authorize sender and recipients */
$item->authorize( array_map( function( $member ) { return (int) $member; }, array_merge( array( \IPS\Request::i()->from ), \IPS\Request::i()->to ) ) );
$item->authorize( array_map( function( $member ) { return (int) $member; }, array_merge( array( Request::i()->from ), Request::i()->to ) ) );
/* Send notifications */
$post->sendNotifications();
return new \IPS\Api\Response( 201, $item->id );
return new Response( 201, $item->id );
}
/**
@@ -103,39 +120,40 @@ class _messages extends \IPS\Api\Controller
* @throws 1C374/6 INVALID_ID The personal conversation ID does not exist
* @throws 1C374/7 INVALID_SENDER The sender ID supplied was not valid
* @throws 1C374/8 SENDER_NO_PERMISSON The sender supplied does not have permmission to reply to the conversation
* @return bool
* @apireturn bool
* @return Response
*/
public function POSTitem( $id )
public function POSTitem( int $id ): Response
{
try
{
$message = \IPS\core\Messenger\Conversation::load( $id );
$message = Conversation::load( $id );
/* Make sure we have a member, and the member is authorized to reply */
if( !isset( \IPS\Request::i()->from ) OR !\IPS\Member::load( (int) \IPS\Request::i()->from )->member_id )
if( !isset( Request::i()->from ) OR !Member::load( (int) Request::i()->from )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_SENDER', '1C374/7', 404 );
throw new Exception( 'INVALID_SENDER', '1C374/7', 404 );
}
if( !$message->canView( \IPS\Member::load( (int) \IPS\Request::i()->from ) ) )
if( !$message->canView( Member::load( (int) Request::i()->from ) ) )
{
throw new \IPS\Api\Exception( 'SENDER_NO_PERMISSON', '1C374/8', 403 );
throw new Exception( 'SENDER_NO_PERMISSON', '1C374/8', 403 );
}
/* Create the reply */
$postContents = \IPS\Text\Parser::parseStatic( \IPS\Request::i()->body, TRUE, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), 'core_Messaging' );
$postContents = Parser::parseStatic( Request::i()->body, NULL, Member::load( (int)Request::i()->from ), 'core_Messaging' );
$commentClass = $message::$commentClass;
$post = $commentClass::create( $message, $postContents, TRUE, NULL, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\DateTime::create() );
$post = $commentClass::create( $message, $postContents, TRUE, NULL, NULL, Member::load( (int) Request::i()->from ), DateTime::create() );
/* Send notifications */
$post->sendNotifications();
return new \IPS\Api\Response( 200, TRUE );
return new Response( 200, TRUE );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C374/6', 404 );
throw new Exception( 'INVALID_ID', '1C374/6', 404 );
}
}
@@ -146,20 +164,21 @@ class _messages extends \IPS\Api\Controller
* @apiclientonly
* @param int $id ID Number
* @throws 1C292/2 INVALID_ID The personal conversation ID does not exist
* @return void
* @apireturn void
* @return Response
*/
public function DELETEitem( $id )
public function DELETEitem( int $id ): Response
{
try
{
$message = \IPS\core\Messenger\Conversation::load( $id );
$message = Conversation::load( $id );
$message->delete();
return new \IPS\Api\Response( 200, NULL );
return new Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C374/1', 404 );
throw new Exception( 'INVALID_ID', '1C374/1', 404 );
}
}
}
+34 -20
View File
@@ -11,16 +11,28 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Exception;
use IPS\Api\PaginatedResponse;
use IPS\Api\Response;
use IPS\core\Feature;
use IPS\Db;
use IPS\Node\Api\NodeController;
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;
}
/**
* @brief Promotions API
*/
class _promotions extends \IPS\Node\Api\NodeController
class promotions extends NodeController
{
/**
* GET /core/promotions
@@ -28,31 +40,32 @@ class _promotions extends \IPS\Node\Api\NodeController
*
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\core\Promote>
* @apireturn PaginatedResponse<IPS\core\Feature>
* @throws 2C429/1 NO_PERMISSION The current authorized user does not have permission to issue warnings and as such cannot view the list of warn reasons
* @return PaginatedResponse<Feature>
*/
public function GETindex()
public function GETindex(): PaginatedResponse
{
$page = isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1;
$perPage = isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : 25;
$page = isset( Request::i()->page ) ? Request::i()->page : 1;
$perPage = isset( Request::i()->perPage ) ? Request::i()->perPage : 25;
$sortDir = ( isset( \IPS\Request::i()->sortDir ) and \in_array( mb_strtolower( \IPS\Request::i()->sortDir ), array( 'asc', 'desc' ) ) ) ? \IPS\Request::i()->sortDir : 'asc';
$sortDir = ( isset( Request::i()->sortDir ) and in_array( mb_strtolower( Request::i()->sortDir ), array( 'asc', 'desc' ) ) ) ? Request::i()->sortDir : 'asc';
$sortBy = ( isset( \IPS\Request::i()->sortBy ) and \in_array( mb_strtolower( \IPS\Request::i()->sortBy ), array( 'promote_id', 'promote_scheduled' ) ) ) ? \IPS\Request::i()->sortBy :'promote_id';
$sortBy = ( isset( Request::i()->sortBy ) and in_array( mb_strtolower( Request::i()->sortBy ), array( 'promote_id', 'promote_scheduled' ) ) ) ? Request::i()->sortBy :'promote_id';
/* Check permissions */
if( $this->member AND !$this->member->modPermission('mod_see_warn') )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2C429/1', 403 );
throw new Exception( 'NO_PERMISSION', '2C429/1', 403 );
}
/* Return */
return new \IPS\Api\PaginatedResponse(
return new PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_social_promote', array(), "{$sortBy} {$sortDir}" ),
Db::i()->select( '*', 'core_content_promote', array(), "{$sortBy} {$sortDir}" ),
$page,
'IPS\core\Promote',
\IPS\Db::i()->select( 'COUNT(*)', 'core_social_promote', array() )->first(),
'IPS\core\Feature',
Db::i()->select( 'COUNT(*)', 'core_content_promote', array() )->first(),
$this->member,
$perPage
);
@@ -64,20 +77,21 @@ class _promotions extends \IPS\Node\Api\NodeController
*
* @param int $id ID Number
* @throws 12C429/2 INVALID_ID The promotion does not exist
* @return \IPS\core\Promote
* @apireturn \IPS\core\Feature
* @return Response
*/
public function GETitem( $id )
public function GETitem( int $id ): Response
{
try
{
$promotion = \IPS\core\Promote::load( $id );
$promotion = Feature::load( $id );
/* Return */
return new \IPS\Api\Response( 200, $promotion->apiOutput( $this->member ) );
return new Response( 200, $promotion->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '2C429/2', 404 );
throw new Exception( 'INVALID_ID', '2C429/2', 404 );
}
}
}
+76 -58
View File
@@ -11,16 +11,32 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\Api\Controller;
use IPS\Api\Response;
use IPS\Content\Search\ApiResponse;
use IPS\Content\Search\ContentFilter;
use IPS\Content\Search\Query;
use IPS\core\modules\front\search\search as FrontModuleSearch;
use IPS\DateTime;
use IPS\Member;
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;
}
/**
* @brief Search API
*/
class _search extends \IPS\Api\Controller
class search extends Controller
{
/**
* GET /core/search
@@ -49,21 +65,22 @@ class _search extends \IPS\Api\Controller
* @apiparam string search_in Specify "titles" to search in titles only, otherwise titles and content are both searched
* @apiparam int search_as Member ID to perform the search as (guest permissions will be used when this parameter is omitted)
* @apiparam bool doNotTrack If doNotTrack is passed with a value of 1, the search will not be tracked for statistical purposes
* @return array
* @apireturn array
* @apiresponse int page api_int_page
* @apiresponse int perPage api_int_perpage
* @apiresponse int totalResults api_int_totalresults
* @apiresponse int totalPages api_int_totalpages
* @apiresponse [\IPS\Content\Search\Result] results api_results_thispage
* @note For requests using an OAuth Access Token for a particular member, only content the authorized user can view will be included and the "search_as" parameter will be ignored.
* @return ApiResponse
*/
public function GETindex()
public function GETindex(): ApiResponse
{
$memberPermissions = $this->member;
if( !$this->member AND isset( \IPS\Request::i()->search_as ) )
if( !$this->member AND isset( Request::i()->search_as ) )
{
$memberPermissions = \IPS\Member::load( \IPS\Request::i()->search_as );
$memberPermissions = Member::load( Request::i()->search_as );
if( !$memberPermissions->member_id )
{
@@ -72,48 +89,48 @@ class _search extends \IPS\Api\Controller
}
/* Get valid content types */
$contentTypes = \IPS\core\modules\front\search\search::contentTypes( $memberPermissions ?: TRUE );
$contentTypes = FrontModuleSearch::contentTypes( $memberPermissions ?: TRUE );
/* Initialize search */
$query = \IPS\Content\Search\Query::init( $memberPermissions ?: NULL );
$query = Query::init( $memberPermissions ?: NULL );
/* Set content type */
if ( isset( \IPS\Request::i()->type ) and array_key_exists( \IPS\Request::i()->type, $contentTypes ) )
if ( isset( Request::i()->type ) and array_key_exists( Request::i()->type, $contentTypes ) )
{
if ( isset( \IPS\Request::i()->item ) )
if ( isset( Request::i()->item ) )
{
$class = $contentTypes[ \IPS\Request::i()->type ];
$class = $contentTypes[ Request::i()->type ];
try
{
$item = $class::loadAndCheckPerms( \IPS\Request::i()->item );
$query->filterByContent( array( \IPS\Content\Search\ContentFilter::init( $class )->onlyInItems( array( \IPS\Request::i()->item ) ) ) );
$item = $class::loadAndCheckPerms( Request::i()->item );
$query->filterByContent( array( ContentFilter::init( $class )->onlyInItems( array( Request::i()->item ) ) ) );
}
catch ( \OutOfRangeException $e ) { }
catch ( OutOfRangeException $e ) { }
}
else
{
$filter = \IPS\Content\Search\ContentFilter::init( $contentTypes[ \IPS\Request::i()->type ] );
$filter = ContentFilter::init( $contentTypes[ Request::i()->type ] );
if ( isset( \IPS\Request::i()->nodes ) )
if ( isset( Request::i()->nodes ) )
{
$filter->onlyInContainers( explode( ',', \IPS\Request::i()->nodes ) );
$filter->onlyInContainers( explode( ',', Request::i()->nodes ) );
}
if ( isset( \IPS\Request::i()->search_min_comments ) )
if ( isset( Request::i()->search_min_comments ) )
{
$filter->minimumComments( \IPS\Request::i()->search_min_comments );
$filter->minimumComments( Request::i()->search_min_comments );
}
if ( isset( \IPS\Request::i()->search_min_replies ) )
if ( isset( Request::i()->search_min_replies ) )
{
$filter->minimumComments( \IPS\Request::i()->search_min_replies + 1 );
$filter->minimumComments( Request::i()->search_min_replies + 1 );
}
if ( isset( \IPS\Request::i()->search_min_reviews ) )
if ( isset( Request::i()->search_min_reviews ) )
{
$filter->minimumReviews( \IPS\Request::i()->search_min_reviews );
$filter->minimumReviews( Request::i()->search_min_reviews );
}
if ( isset( \IPS\Request::i()->search_min_views ) )
if ( isset( Request::i()->search_min_views ) )
{
$filter->minimumViews( \IPS\Request::i()->search_min_views );
$filter->minimumViews( Request::i()->search_min_views );
}
$query->filterByContent( array( $filter ) );
@@ -121,9 +138,9 @@ class _search extends \IPS\Api\Controller
}
/* Filter by author */
if ( isset( \IPS\Request::i()->author ) )
if ( isset( Request::i()->author ) )
{
$author = \IPS\Member::load( \IPS\Request::i()->author, 'name' );
$author = Member::load( Request::i()->author, 'name' );
if ( $author->member_id )
{
$query->filterByAuthor( $author );
@@ -131,9 +148,9 @@ class _search extends \IPS\Api\Controller
}
/* Filter by club */
if ( isset( \IPS\Request::i()->club ) AND \IPS\Settings::i()->clubs )
if ( isset( Request::i()->club ) AND Settings::i()->clubs )
{
$query->filterByClub( explode( ',', \IPS\Request::i()->club ) );
$query->filterByClub( explode( ',', Request::i()->club ) );
}
/* Set time cutoffs */
@@ -141,38 +158,38 @@ class _search extends \IPS\Api\Controller
{
$beforeKey = "{$k}_before";
$afterKey = "{$k}_after";
if ( isset( \IPS\Request::i()->$beforeKey ) or isset( \IPS\Request::i()->$afterKey ) )
if ( isset( Request::i()->$beforeKey ) or isset( Request::i()->$afterKey ) )
{
foreach ( array( 'before', 'after' ) as $l )
{
$$l = NULL;
$key = "{$l}Key";
if ( isset( \IPS\Request::i()->$$key ) AND \IPS\Request::i()->$$key != 'any' )
if ( isset( Request::i()->$$key ) AND Request::i()->$$key != 'any' )
{
switch ( \IPS\Request::i()->$$key )
switch ( Request::i()->$$key )
{
case 'day':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1D' ) );
$$l = DateTime::create()->sub( new DateInterval( 'P1D' ) );
break;
case 'week':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1W' ) );
$$l = DateTime::create()->sub( new DateInterval( 'P1W' ) );
break;
case 'month':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1M' ) );
$$l = DateTime::create()->sub( new DateInterval( 'P1M' ) );
break;
case 'six_months':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P6M' ) );
$$l = DateTime::create()->sub( new DateInterval( 'P6M' ) );
break;
case 'year':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1Y' ) );
$$l = DateTime::create()->sub( new DateInterval( 'P1Y' ) );
break;
default:
$$l = \IPS\DateTime::ts( \IPS\Request::i()->$$key );
$$l = DateTime::ts( Request::i()->$$key );
break;
}
}
@@ -183,44 +200,44 @@ class _search extends \IPS\Api\Controller
}
/* Set Order */
if ( ! isset( \IPS\Request::i()->sortby ) )
if ( ! isset( Request::i()->sortby ) )
{
\IPS\Request::i()->sortby = $query->getDefaultSortMethod();
Request::i()->sortby = $query->getDefaultSortMethod();
}
switch( \IPS\Request::i()->sortby )
switch( Request::i()->sortby )
{
case 'newest':
$query->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_CREATED );
$query->setOrder( Query::ORDER_NEWEST_CREATED );
break;
case 'relevancy':
$query->setOrder( \IPS\Content\Search\Query::ORDER_RELEVANCY );
$query->setOrder( Query::ORDER_RELEVANCY );
break;
}
$flags = ( isset( \IPS\Request::i()->eitherTermsOrTags ) and \IPS\Request::i()->eitherTermsOrTags === 'and' ) ? \IPS\Content\Search\Query::TERM_AND_TAGS : \IPS\Content\Search\Query::TERM_OR_TAGS;
$flags = ( isset( Request::i()->eitherTermsOrTags ) and Request::i()->eitherTermsOrTags === 'and' ) ? Query::TERM_AND_TAGS : Query::TERM_OR_TAGS;
$operator = NULL;
if ( isset( \IPS\Request::i()->search_and_or ) and \in_array( \IPS\Request::i()->search_and_or, array( \IPS\Content\Search\Query::OPERATOR_OR, \IPS\Content\Search\Query::OPERATOR_AND ) ) )
if ( isset( Request::i()->search_and_or ) and in_array( Request::i()->search_and_or, array( Query::OPERATOR_OR, Query::OPERATOR_AND ) ) )
{
$operator = \IPS\Request::i()->search_and_or;
$operator = Request::i()->search_and_or;
}
if ( isset( \IPS\Request::i()->search_in ) and \IPS\Request::i()->search_in === 'titles' )
if ( isset( Request::i()->search_in ) and Request::i()->search_in === 'titles' )
{
$flags = $flags | \IPS\Content\Search\Query::TERM_TITLES_ONLY;
$flags = $flags | Query::TERM_TITLES_ONLY;
}
/* Return */
return new \IPS\Content\Search\ApiResponse(
return new ApiResponse(
200,
array( $query, $flags, isset( \IPS\Request::i()->q ) ? ( \IPS\Request::i()->q ) : NULL, isset( \IPS\Request::i()->tags ) ? explode( ',', \IPS\Request::i()->tags ) : NULL, $operator ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
array( $query, $flags, isset( Request::i()->q ) ? ( Request::i()->q ) : NULL, isset( Request::i()->tags ) ? explode( ',', Request::i()->tags ) : NULL, $operator ),
isset( Request::i()->page ) ? Request::i()->page : 1,
NULL,
0,
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
isset( Request::i()->perPage ) ? Request::i()->perPage : NULL
);
}
@@ -229,16 +246,17 @@ class _search extends \IPS\Api\Controller
* Get list of content types that can be searched
*
* @clientapiparam int search_as Member ID to perform the search as (by default, search results are based on guest permissions)
* @return array
* @apireturn array
* @apiresponse array contenttypes Content types that can be used in /search requests in the 'type' parameter
* @return Response
*/
public function GETitem()
public function GETitem(): Response
{
$memberPermissions = $this->member;
if( !$this->member AND isset( \IPS\Request::i()->search_as ) )
if( !$this->member AND isset( Request::i()->search_as ) )
{
$memberPermissions = \IPS\Member::load( \IPS\Request::i()->search_as );
$memberPermissions = Member::load( Request::i()->search_as );
if( !$memberPermissions->member_id )
{
@@ -246,6 +264,6 @@ class _search extends \IPS\Api\Controller
}
}
return new \IPS\Api\Response( 200, array( 'contenttypes' => array_keys( \IPS\core\modules\front\search\search::contentTypes( $memberPermissions ?: TRUE ) ) ) );
return new Response( 200, array( 'contenttypes' => array_keys( FrontModuleSearch::contentTypes( $memberPermissions ?: TRUE ) ) ) );
}
}
+56 -38
View File
@@ -11,21 +11,33 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Exception;
use IPS\Api\PaginatedResponse;
use IPS\Api\Response;
use IPS\core\Warnings\Reason;
use IPS\Lang;
use IPS\Node\Api\NodeController;
use IPS\Node\Model;
use IPS\Request;
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;
}
/**
* @brief Warn Reasons API
*/
class _warnreasons extends \IPS\Node\Api\NodeController
class warnreasons extends NodeController
{
/**
* Class
*/
protected $class = 'IPS\core\Warnings\Reason';
protected string $class = 'IPS\core\Warnings\Reason';
/**
* GET /core/warnreasons
@@ -33,15 +45,16 @@ class _warnreasons extends \IPS\Node\Api\NodeController
*
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\core\Warnings\Reason>
* @apireturn PaginatedResponse<IPS\core\Warnings\Reason>
* @throws 1C292/S NO_PERMISSION The current authorized user does not have permission to issue warnings and as such cannot view the list of warn reasons
* @return PaginatedResponse<Reason>
*/
public function GETindex()
public function GETindex() : PaginatedResponse
{
/* Check permissions */
if( $this->member AND ( !$this->member->modPermission('mod_can_warn') OR !$this->member->modPermission('mod_see_warn') ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '1C292/S', 403 );
throw new Exception( 'NO_PERMISSION', '1C292/S', 403 );
}
/* Return */
@@ -55,17 +68,18 @@ class _warnreasons extends \IPS\Node\Api\NodeController
* @apiclientonly
* @param int $id ID Number
* @throws 1C385/1 INVALID_ID The warn reason does not exist
* @return \IPS\core\Warnings\Reason
* @apireturn \IPS\core\Warnings\Reason
* @return Response
*/
public function GETitem( $id )
public function GETitem( int $id ) : Response
{
try
{
return $this->_view( $id );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C385/1', 404 );
throw new Exception( 'INVALID_ID', '1C385/1', 404 );
}
}
@@ -81,23 +95,24 @@ class _warnreasons extends \IPS\Node\Api\NodeController
* @apiparam bool pointsAutoRemove Whether or not to automatically remove points (defaults to false). If true, you must supply removePoints.
* @apiparam string|null removePoints Timeframe to remove points after as a date interval (e.g. P2D for 2 days or PT6H for 6 hours)
* @apiparam bool removeOverride Whether or not moderators can override the default points removal configuration
* @return \IPS\core\Warnings\Reason
* @apireturn \IPS\core\Warnings\Reason
* @throws 1C385/2 INVALID_POINTS_EXPIRATION Points were specified as automatically removing but the removal time period was not supplied or is invalid
* @throws 1C385/4 NO_NAME A name for the warn reason must be supplied
* @return Response
*/
public function POSTindex()
public function POSTindex() : Response
{
if( !\IPS\Request::i()->name )
if( !Request::i()->name )
{
throw new \IPS\Api\Exception( 'NO_NAME', '1C385/4', 400 );
throw new Exception( 'NO_NAME', '1C385/4', 400 );
}
if( isset( \IPS\Request::i()->pointsAutoRemove ) AND \IPS\Request::i()->pointsAutoRemove AND ( !isset( \IPS\Request::i()->removePoints ) OR !\IPS\Request::i()->removePoints ) )
if( isset( Request::i()->pointsAutoRemove ) AND Request::i()->pointsAutoRemove AND ( !isset( Request::i()->removePoints ) OR !Request::i()->removePoints ) )
{
throw new \IPS\Api\Exception( 'INVALID_POINTS_EXPIRATION', '1C385/2', 400 );
throw new Exception( 'INVALID_POINTS_EXPIRATION', '1C385/2', 400 );
}
return new \IPS\Api\Response( 201, $this->_create()->apiOutput( $this->member ) );
return new Response( 201, $this->_create()->apiOutput( $this->member ) );
}
/**
@@ -113,20 +128,22 @@ class _warnreasons extends \IPS\Node\Api\NodeController
* @apiparam string|null removePoints Timeframe to remove points after as a date interval (e.g. P2D for 2 days or PT6H for 6 hours)
* @apiparam bool removeOverride Whether or not moderators can override the default points removal configuration
* @param int $id ID Number
* @return \IPS\core\Warnings\Reason
* @apireturn \IPS\core\Warnings\Reason
* @throws 1C385/3 INVALID_POINTS_EXPIRATION Points were specified as automatically removing but the removal time period was not supplied or is invalid
* @return Response
*/
public function POSTitem( $id )
public function POSTitem( int $id ) : Response
{
if( isset( \IPS\Request::i()->pointsAutoRemove ) AND \IPS\Request::i()->pointsAutoRemove AND ( !isset( \IPS\Request::i()->removePoints ) OR !\IPS\Request::i()->removePoints ) )
if( isset( Request::i()->pointsAutoRemove ) AND Request::i()->pointsAutoRemove AND ( !isset( Request::i()->removePoints ) OR !Request::i()->removePoints ) )
{
throw new \IPS\Api\Exception( 'INVALID_POINTS_EXPIRATION', '1C385/3', 400 );
throw new Exception( 'INVALID_POINTS_EXPIRATION', '1C385/3', 400 );
}
/* @var Reason $class */
$class = $this->class;
$reason = $class::load( $id );
return new \IPS\Api\Response( 200, $this->_createOrUpdate( $reason )->apiOutput( $this->member ) );
return new Response( 200, $this->_createOrUpdate( $reason )->apiOutput( $this->member ) );
}
/**
@@ -135,9 +152,10 @@ class _warnreasons extends \IPS\Node\Api\NodeController
*
* @apiclientonly
* @param int $id ID Number
* @return void
* @apireturn void
* @return Response
*/
public function DELETEitem( $id )
public function DELETEitem( int $id ) : Response
{
return $this->_delete( $id );
}
@@ -145,33 +163,33 @@ class _warnreasons extends \IPS\Node\Api\NodeController
/**
* Create or update node
*
* @param \IPS\node\Model $reason The node
* @return \IPS\node\Model
* @param Model $reason The node
* @return Model
*/
protected function _createOrUpdate( \IPS\Node\Model $reason )
protected function _createOrUpdate( Model $reason ): Model
{
if( \IPS\Request::i()->name )
if( Request::i()->name )
{
\IPS\Lang::saveCustom( 'core', 'core_warn_reason_' . $reason->id, \IPS\Request::i()->name );
Lang::saveCustom( 'core', 'core_warn_reason_' . $reason->id, Request::i()->name );
}
$reason->points = (int) \IPS\Request::i()->points;
$reason->points_override = (bool) \IPS\Request::i()->pointsOverride;
$reason->remove_override = (bool) \IPS\Request::i()->removeOverride;
$reason->notes = \IPS\Request::i()->notes;
$reason->points = (int) Request::i()->points;
$reason->points_override = (bool) Request::i()->pointsOverride;
$reason->remove_override = (bool) Request::i()->removeOverride;
$reason->notes = Request::i()->notes;
if( isset( \IPS\Request::i()->pointsAutoRemove ) OR isset( \IPS\Request::i()->removePoints ) )
if( isset( Request::i()->pointsAutoRemove ) OR isset( Request::i()->removePoints ) )
{
if( !\IPS\Request::i()->pointsAutoRemove )
if( !Request::i()->pointsAutoRemove )
{
$reason->remove = -1;
$reason->remove_unit = NULL;
}
else
{
$reason->remove = (int) str_ireplace( array( 'P', 'T', 'H' ), '', \IPS\Request::i()->removePoints );
$reason->remove = (int) str_ireplace( array( 'P', 'T', 'H' ), '', Request::i()->removePoints );
if( mb_strpos( \IPS\Request::i()->removePoints, 'PT' ) === 0 )
if( mb_strpos( Request::i()->removePoints, 'PT' ) === 0 )
{
$reason->remove_unit = 'h';
}
+34 -22
View File
@@ -11,16 +11,26 @@
namespace IPS\core\api;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Api\Controller;
use IPS\Api\Exception;
use IPS\Api\Response;
use IPS\Api\Webhook;
use IPS\Http\Url;
use IPS\Request;
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;
}
/**
* @brief Webhooks API
*/
class _webhooks extends \IPS\Api\Controller
class webhooks extends Controller
{
/**
* POST /core/webhooks
@@ -30,39 +40,40 @@ class _webhooks extends \IPS\Api\Controller
* @reqapiparam array events List of events to subscribe to
* @reqapiparam string url URL to send webhook to
* @apiparam string content_header The content type for the request.
* @return \IPS\Api\Webhook
* @apireturn \IPS\Api\Webhook
* @throws 1C293/1 NO_EVENTS No events were specified
* @throws 1C293/2 INVALID_URL The URL specified was not valid
* @return Response
*/
public function POSTindex()
public function POSTindex(): Response
{
$events = \IPS\Request::i()->events;
$events = Request::i()->events;
if ( !$events )
{
throw new \IPS\Api\Exception( 'NO_EVENTS', '1C293/1', 400 );
throw new Exception( 'NO_EVENTS', '1C293/1', 400 );
}
try
{
$url = new \IPS\Http\Url( \IPS\Request::i()->url );
$url = new Url( Request::i()->url );
}
catch ( \IPS\Http\Url\Exception $e )
catch ( Url\Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_URL', '1C293/2', 400 );
throw new Exception( 'INVALID_URL', '1C293/2', 400 );
}
$webhook = new \IPS\Api\Webhook;
$webhook = new Webhook;
$webhook->api_key = $this->apiKey;
$webhook->events = $events;
$webhook->filters = ( \IPS\Request::i()->filters ?: array() );
$webhook->filters = ( Request::i()->filters ?: array() );
$webhook->url = $url;
if( \IPS\Request::i()->content_header )
if( Request::i()->content_header )
{
$webhook->content_type = \IPS\Request::i()->content_header;
$webhook->content_type = Request::i()->content_header;
}
$webhook->save();
return new \IPS\Api\Response( 201, $webhook->apiOutput() );
return new Response( 201, $webhook->apiOutput() );
}
/**
@@ -71,28 +82,29 @@ class _webhooks extends \IPS\Api\Controller
*
* @apiclientonly
* @param int $id ID Number
* @return void
* @apireturn void
* @throws 1C293/3 INVALID_ID The ID provided does not match any webhook
* @throws 3C293/4 WRONG_API_KEY The API key making this request is not the API key that created the webhook
* @return Response
*/
public function DELETEitem( $id )
public function DELETEitem( int $id ): Response
{
try
{
$webhook = \IPS\Api\Webhook::load( $id );
$webhook = Webhook::load( $id );
if ( $webhook->api_key != $this->apiKey )
{
throw new \IPS\Api\Exception( 'WRONG_API_KEY', '3C293/4', 403 );
throw new Exception( 'WRONG_API_KEY', '3C293/4', 403 );
}
$webhook->delete();
return new \IPS\Api\Response( 200, NULL );
return new Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C293/3', 404 );
throw new Exception( 'INVALID_ID', '1C293/3', 404 );
}
}
}