Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Core mutations
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\Types;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core mutationss GraphQL API
|
||||
*/
|
||||
abstract class _Mutation
|
||||
{
|
||||
/**
|
||||
* Get the supported query types in this app
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function mutations()
|
||||
{
|
||||
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(),
|
||||
'sessionStart' => new \IPS\core\api\GraphQL\Mutations\SessionStart(),
|
||||
'sessionEnd' => new \IPS\core\api\GraphQL\Mutations\SessionEnd(),
|
||||
'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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Change a notification setting
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 4 Aug 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change notification setting mutation for GraphQL API
|
||||
*/
|
||||
class _ChangeNotificationSetting
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Change a notification setting";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'extension' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'type' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'email' => TypeRegistry::boolean(),
|
||||
'push' => TypeRegistry::boolean(),
|
||||
'inline' => TypeRegistry::boolean(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::notificationType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
$pieces = explode('_', $args['extension']);
|
||||
|
||||
if( \count( $pieces ) !== 2 )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
$extensions = \IPS\Application::load( $pieces[0] )->extensions('core', 'Notifications');
|
||||
|
||||
if( !isset( $extensions[ $pieces[1] ] ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_EXTENSION', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
$extensionOptions = \IPS\Notification::availableOptions( \IPS\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 );
|
||||
}
|
||||
|
||||
$value = array();
|
||||
$extensionType = $extensionOptions[ $args['type'] ];
|
||||
$options = $extensionType['options'];
|
||||
|
||||
foreach( $options as $type => $typeSettings )
|
||||
{
|
||||
// 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'] ) )
|
||||
{
|
||||
$value[ $type ] = $type;
|
||||
}
|
||||
}
|
||||
// If we're taking the existing value
|
||||
else if( $typeSettings['value'] === TRUE )
|
||||
{
|
||||
$value[ $type ] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
// If push is true, inline must also be true
|
||||
if( isset( $value['push'] ) )
|
||||
{
|
||||
$value['inline'] = 'inline';
|
||||
}
|
||||
|
||||
foreach ( $extensionType['notificationTypes'] as $notificationKey )
|
||||
{
|
||||
\IPS\Db::i()->insert( 'core_notification_preferences', array(
|
||||
'member_id' => \IPS\Member::loggedIn()->member_id,
|
||||
'notification_key' => $notificationKey,
|
||||
'preference' => implode( ',', $value )
|
||||
), TRUE );
|
||||
}
|
||||
|
||||
// Get the data we need to return
|
||||
$methods = array('inline' => array(), 'email' => array(), 'push' => array());
|
||||
|
||||
foreach( $methods as $method => $methodData )
|
||||
{
|
||||
if( !isset( $options[ $method ] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$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 ]['member'] = isset( $value[ $method ] );
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $args['id'],
|
||||
'extension' => $args['extension'],
|
||||
'type' => $type,
|
||||
'name' => $extensionType['title'],
|
||||
'description' => $extensionType['description'],
|
||||
'inline' => $methods['inline'],
|
||||
'email' => $methods['email'],
|
||||
'push' => $methods['push']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Delete attachment mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 22 May 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete attachment mutation for GraphQL API
|
||||
*/
|
||||
class _DeleteAttachment
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Delete an attachment";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
'editorLocation' => TypeRegistry::string(),
|
||||
'locationId1' => TypeRegistry::int(),
|
||||
'locationId2' => TypeRegistry::int(),
|
||||
'locationId3' => TypeRegistry::string(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::boolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args, $context, $info)
|
||||
{
|
||||
/* Get the attachment */
|
||||
try
|
||||
{
|
||||
$attachment = \IPS\Db::i()->select( '*', 'core_attachments', array( 'attach_id=?', $args['id'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_ATTACHMENT', '2C398/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 */
|
||||
if ( isset( $args['editorLocation'] ) and ( isset( $args['locationId1'] ) or isset( $args['locationId2'] ) or isset( $args['locationId3'] ) ) )
|
||||
{
|
||||
$where = array( array( 'location_key=?', $args['editorLocation'] ), array( 'attachment_id=?', $attachment['attach_id'] ) );
|
||||
|
||||
foreach ( range( 1, 3 ) as $i )
|
||||
{
|
||||
if ( isset( $args["locationId{$i}"] ) )
|
||||
{
|
||||
$where[] = array( "id{$i}=?", $args["locationId{$i}"] );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\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 )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'CANNOT_DELETE_OTHERS_ATTACHMENTS', '2C398/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();
|
||||
if ( !$otherMaps )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_attachments', array( 'attach_id=?', $attachment['attach_id'] ) );
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->delete();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
if ( $attachment['attach_thumb_location'] )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->delete();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
/* Return that we're done */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Follow something mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 9 Sep 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow something mutation for GraphQL API
|
||||
*/
|
||||
class _Follow
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Follow a node, item or member";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'app' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'area' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
'anonymous' => TypeRegistry::nonNull( TypeRegistry::boolean() ),
|
||||
'type' => TypeRegistry::nonNull( TypeRegistry::eNum([
|
||||
'name' => 'core_Follow_followOptions',
|
||||
'values' => ['IMMEDIATE', 'WEEKLY', 'DAILY', 'NONE'],
|
||||
'defaultValue' => 'IMMEDIATE'
|
||||
]))
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::follow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
* @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
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0001/6', 403 );
|
||||
}
|
||||
|
||||
/* Get class */
|
||||
$class = NULL;
|
||||
foreach ( \IPS\Application::load( $args['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
|
||||
{
|
||||
foreach ( $ext->classes as $classname )
|
||||
{
|
||||
if ( $classname == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $classname;
|
||||
break;
|
||||
}
|
||||
if ( isset( $classname::$containerNodeClass ) and $classname::$containerNodeClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $classname::$containerNodeClass;
|
||||
break;
|
||||
}
|
||||
if( isset( $classname::$containerFollowClasses ) )
|
||||
{
|
||||
foreach( $classname::$containerFollowClasses as $followClass )
|
||||
{
|
||||
if( $followClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $followClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $args['app'] == 'core' and $args['area'] == 'member' )
|
||||
{
|
||||
/* You can't follow yourself */
|
||||
if( $args['id'] == \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'CANT_FOLLOW_SELF', 'GQL/0001/1', 403 );
|
||||
}
|
||||
|
||||
/* Following disabled */
|
||||
$member = \IPS\Member::load( $args['id'] );
|
||||
|
||||
if( !$member->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\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 ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'CANT_FOLLOW_MEMBER', 'GQL/0001/3', 403 );
|
||||
}
|
||||
|
||||
$class = 'IPS\\Member';
|
||||
}
|
||||
|
||||
if( $args['app'] == 'core' and $args['area'] == 'club' )
|
||||
{
|
||||
$class = 'IPS\Member\Club';
|
||||
}
|
||||
|
||||
if ( !$class )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_FOUND', 'GQL/0001/4', 404 );
|
||||
}
|
||||
|
||||
/* Get our return info ready */
|
||||
$return = array(
|
||||
'app' => $args['app'],
|
||||
'area' => $args['area'],
|
||||
'id' => $args['id']
|
||||
);
|
||||
|
||||
/* Get thing */
|
||||
$thing = NULL;
|
||||
try
|
||||
{
|
||||
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
$thing = $class::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
elseif ( $class == 'IPS\Member\Club' )
|
||||
{
|
||||
$thing = $class::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
elseif ( $class != "IPS\Member" )
|
||||
{
|
||||
$thing = $class::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$thing = $class::load( $args['id'] );
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\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();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$current = FALSE;
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
$save = array(
|
||||
'follow_id' => md5( $args['app'] . ';' . $args['area'] . ';' . $args['id'] . ';' . \IPS\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_is_anon' => $args['anonymous'],
|
||||
'follow_added' => time(),
|
||||
'follow_notify_do' => $args['type'] == 'NONE' ? 0 : 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => ( $class == "IPS\Member" ) ? 'immediate' : mb_strtolower($args['type']),
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1,
|
||||
);
|
||||
if ( $current )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_follow', $save, array( 'follow_id=?', $current['follow_id'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->insert( 'core_follow', $save );
|
||||
}
|
||||
|
||||
/* Also follow all nodes if following club */
|
||||
if( $class == "IPS\Member\Club" )
|
||||
{
|
||||
foreach ( $thing->nodes() as $node )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
$save = array(
|
||||
'follow_id' => md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . \IPS\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_is_anon' => $args['anonymous'],
|
||||
'follow_added' => time(),
|
||||
'follow_notify_do' => $args['type'] == 'NONE' ? 0 : 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => mb_strtolower($args['type']),
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1,
|
||||
);
|
||||
\IPS\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->recipients->attach( $thing );
|
||||
$notification->send();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'node' => $thing,
|
||||
'nodeClass' => \get_class( $thing )
|
||||
));
|
||||
}
|
||||
else if( $class == 'IPS\Member' )
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'member' => $thing
|
||||
));
|
||||
}
|
||||
else if( $class == 'IPS\Member\Club' )
|
||||
{
|
||||
// @future Support club follows
|
||||
}
|
||||
else
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'item' => $thing,
|
||||
'itemClass' => \get_class( $thing )
|
||||
));
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Ignore user mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 29 May 2020
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore user mutation for GraphQL API
|
||||
*/
|
||||
class _IgnoreUser
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Ignore a member";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'member' => TypeRegistry::nonNull( TypeRegistry::int() ),
|
||||
'type' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'isIgnoring' => TypeRegistry::nonNull( TypeRegistry::boolean() )
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::ignoreOption();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if ( !\in_array( $args['type'], \IPS\core\Ignore::types() ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_TYPE', 'GQL/0001/4', 404 );
|
||||
}
|
||||
|
||||
$type = $args['type'];
|
||||
$member = \IPS\Member::load( $args['member'] );
|
||||
|
||||
if( !$member->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_MEMBER', 'GQL/0001/4', 403 );
|
||||
}
|
||||
|
||||
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_IGNORE_SELF', 'GQL/0001/4', 403 );
|
||||
}
|
||||
|
||||
if ( !$member->canBeIgnored() )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_IGNORE_MEMBER', 'GQL/0001/4', 403 );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
$ignore->$type = $args['isIgnoring'];
|
||||
$ignore->save();
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$ignore = new \IPS\core\Ignore;
|
||||
$ignore->$type = $args['isIgnoring'];
|
||||
$ignore->owner_id = \IPS\Member::loggedIn()->member_id;
|
||||
$ignore->ignore_id = $member->member_id;
|
||||
$ignore->save();
|
||||
}
|
||||
|
||||
$return = array(
|
||||
'type' => $type,
|
||||
'is_being_ignored' => $args['isIgnoring']
|
||||
);
|
||||
|
||||
\IPS\Member::loggedIn()->members_bitoptions['has_no_ignored_users'] = FALSE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Mark a notification read mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 6 Nov 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark notification read mutation for GraphQL API
|
||||
*/
|
||||
class _MarkNotificationRead
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Mark a notification as read";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::int()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::notification();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\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 );
|
||||
|
||||
if( isset( $args['id'] ) && $args['id'] !== NULL )
|
||||
{
|
||||
$where[] = array( "id = ?", \intval( $args['id'] ) );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$row = \IPS\Db::i()->select( '*', 'core_notifications', $where )->first();
|
||||
$notification = \IPS\Notification\Api::constructFromData( $row );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
if( isset( $args['id'] ) && $args['id'] !== NULL )
|
||||
{
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'core_notifications', array( 'read_time' => time() ), $where );
|
||||
\IPS\Member::loggedIn()->recountNotifications();
|
||||
|
||||
if( $notification )
|
||||
{
|
||||
return array( 'notification' => $notification, 'data' => $notification->getData() );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Add user to PM conversation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 23 Oct 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations\Messenger;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add user to conversation mutation for GraphQL API
|
||||
*/
|
||||
class _AddConversationUser
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Add a user to a PM conversation";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
'memberId' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2', 400 );
|
||||
}
|
||||
|
||||
// Fetch our new member
|
||||
$member = \IPS\Member::load( $args['memberId'] );
|
||||
|
||||
if( !$member->member_id ){
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_MEMBER', '1F294/2', 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'] )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'TOO_MANY_RECIPIENTS', '1F294/2', 400 );
|
||||
}
|
||||
|
||||
$maps = $conversation->maps( TRUE );
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
if ( !$this->memberCanReceiveNewMessage( $member ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'BAD_RECIPIENT', '1F294/2', 403 );
|
||||
}
|
||||
|
||||
//echo 'here';
|
||||
|
||||
$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->recipients->attach( $member );
|
||||
$notification->send();
|
||||
|
||||
return $conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the member recieve new messages?
|
||||
*
|
||||
* @param \IPS\Member The member to check
|
||||
* @return boolean
|
||||
*/
|
||||
public function memberCanReceiveNewMessage( $member )
|
||||
{
|
||||
if ( $member->members_disable_pm )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Group can not use messenger */
|
||||
if ( !$member->canAccessModule( \IPS\Application\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'] )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Is being ignored */
|
||||
if ( $member->isIgnoring( \IPS\Member::loggedIn(), 'messages' ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Leave a PM conversation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 23 Oct 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations\Messenger;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave conversation mutation for GraphQL API
|
||||
*/
|
||||
class _LeaveConversation
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Leave a PM conversation";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() )
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
|
||||
$conversation->deauthorize( \IPS\Member::loggedIn() );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2', 400 );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Remove a user from a conversation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 23 Oct 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations\Messenger;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user from conversation mutation for GraphQL API
|
||||
*/
|
||||
class _RemoveConversationUser
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Leave a PM conversation";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
'memberId' => TypeRegistry::id()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::messengerConversation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2', 400 );
|
||||
}
|
||||
|
||||
if( $conversation->starter_id !== \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_OWNER', 'GQL/0003/1', 403 );
|
||||
}
|
||||
|
||||
$conversation->deauthorize( \IPS\Member::load( $args['memberId'] ), TRUE );
|
||||
$conversation->maps(TRUE); // Need to rebuild participant maps here to reflect this change
|
||||
|
||||
return $conversation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: End a session for the user
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 02 Apr 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push token for a specific user
|
||||
*/
|
||||
class _SessionEnd
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "End a user session";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'token' => TypeRegistry::string()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::member();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
* @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
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0001/6', 403 );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=? AND access_token=?', \IPS\Member::loggedIn()->member_id, \IPS\Dispatcher::i()->accessToken['access_token'] ) );
|
||||
if ( isset( \IPS\Dispatcher::i()->accessToken['device_key'] ) )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', \IPS\Member::loggedIn()->member_id, \IPS\Dispatcher::i()->accessToken['device_key'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $args['token'] ) and $args['token'] )
|
||||
{
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::ips('mobile/signout')->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->post( array(
|
||||
'member' => \IPS\Member::loggedIn()->member_id,
|
||||
'token' => $args['token']
|
||||
) )->decodeJson();
|
||||
|
||||
if ( $response->httpResponseCode != 200 )
|
||||
{
|
||||
throw new \DomainException( (string) $response );
|
||||
}
|
||||
|
||||
if ( !$response['remainingDevices'] )
|
||||
{
|
||||
\IPS\Member::loggedIn()->members_bitoptions['mobile_notifications'] = FALSE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'mobile-push-notifications' );
|
||||
}
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Start a session for a user
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 20 Mar 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push token for a specific user
|
||||
*/
|
||||
class _SessionStart
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Start a session for an authenticated user";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'token' => TypeRegistry::string()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\Api\GraphQL\TypeRegistry::member();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
* @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
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_LOGGED_IN', 'GQL/0001/6', 403 );
|
||||
}
|
||||
|
||||
if ( isset( $args['token'] ) and $args['token'] )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Register the token with the backend */
|
||||
$response = \IPS\Http\Url::ips('mobile/signin')->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->post( array(
|
||||
'member' => \IPS\Member::loggedIn()->member_id,
|
||||
'token' => $args['token']
|
||||
) );
|
||||
if ( !\in_array( $response->httpResponseCode, array( 200, 201 ) ) )
|
||||
{
|
||||
throw new \DomainException( (string) $response );
|
||||
}
|
||||
|
||||
/* If we didn't already have notifications enabled, enable them and set the defaults */
|
||||
if ( !\IPS\Member::loggedIn()->members_bitoptions['mobile_notifications'] )
|
||||
{
|
||||
\IPS\Member::loggedIn()->members_bitoptions['mobile_notifications'] = TRUE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
|
||||
$notificationPreferences = \IPS\Db::i()->select( '*', 'core_notification_preferences', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
if ( \count( $notificationPreferences ) )
|
||||
{
|
||||
$keysToAddPushTo = array();
|
||||
|
||||
foreach ( $notificationPreferences as $row )
|
||||
{
|
||||
if ( \in_array( 'inline', explode( ',', $row['preference'] ) ) )
|
||||
{
|
||||
$keysToAddPushTo[] = $row['notification_key'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $keysToAddPushTo ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_notification_preferences', "`preference` = IF( `preference` = '', 'push', CONCAT_WS( ',', `preference`, 'push' ) )", array( array( 'member_id=?', \IPS\Member::loggedIn()->member_id ), array( \IPS\Db::i()->in( 'notification_key', $keysToAddPushTo ) ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'mobile-push-notifications' );
|
||||
}
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Unfollow something mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 9 Sep 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow something mutation for GraphQL API
|
||||
*/
|
||||
class _Unfollow
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Unfollow a node, item or member";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'app' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'area' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() ),
|
||||
'followID' => TypeRegistry::nonNull( TypeRegistry::id() )
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::follow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
* @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
|
||||
* @return \IPS\forums\Forum
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\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();
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', $args['followID'], \IPS\Member::loggedIn()->member_id ) );
|
||||
|
||||
/* Get class */
|
||||
if( $args['app'] == 'core' and $args['area'] == 'member' )
|
||||
{
|
||||
$class = 'IPS\\Member';
|
||||
}
|
||||
elseif( $args['app'] == 'core' and $args['area'] == 'club' )
|
||||
{
|
||||
$class = 'IPS\\Member\Club';
|
||||
}
|
||||
else
|
||||
{
|
||||
$class = NULL;
|
||||
foreach ( \IPS\Application::load( $args['app'] )->extensions( 'core', 'ContentRouter' ) as $ext )
|
||||
{
|
||||
foreach ( $ext->classes as $classname )
|
||||
{
|
||||
if ( $classname == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $classname;
|
||||
break;
|
||||
}
|
||||
if ( isset( $classname::$containerNodeClass ) and $classname::$containerNodeClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $classname::$containerNodeClass;
|
||||
break;
|
||||
}
|
||||
if( isset( $classname::$containerFollowClasses ) )
|
||||
{
|
||||
foreach( $classname::$containerFollowClasses as $followClass )
|
||||
{
|
||||
if( $followClass == 'IPS\\' . $args['app'] . '\\' . mb_ucfirst( $args['area'] ) )
|
||||
{
|
||||
$class = $followClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get our return info ready */
|
||||
$return = array(
|
||||
'app' => $args['app'],
|
||||
'area' => $args['area'],
|
||||
'id' => $args['id']
|
||||
);
|
||||
|
||||
/* Get thing */
|
||||
$thing = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
if ( $class != "IPS\Member" )
|
||||
{
|
||||
$thing = $class::loadAndCheckPerms( $follow['follow_rel_id'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$thing = $class::load( $follow['follow_rel_id'] );
|
||||
}
|
||||
|
||||
/* Unfollow club areas */
|
||||
if( $class == "IPS\Member\Club" )
|
||||
{
|
||||
foreach ( $thing->nodes() as $node )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
\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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'node' => $thing,
|
||||
'nodeClass' => \get_class( $thing )
|
||||
));
|
||||
}
|
||||
else if( $class == 'IPS\Member' )
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'member' => $thing
|
||||
));
|
||||
}
|
||||
else if( $class == 'IPS\Member\Club' )
|
||||
{
|
||||
// @future Support clubs
|
||||
}
|
||||
else
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'item' => $thing,
|
||||
'itemClass' => \get_class( $thing )
|
||||
));
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Upload attachment mutation
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 22 May 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Mutations;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload attachment mutation for GraphQL API
|
||||
*/
|
||||
class _UploadAttachment
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Upload a file to be an attachment";
|
||||
|
||||
/*
|
||||
* Mutation arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'name' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'contents' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'postKey' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
'chunk' => TypeRegistry::int(),
|
||||
'totalChunks' => TypeRegistry::int(),
|
||||
'ref' => TypeRegistry::nonNull( TypeRegistry::string() ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mutation return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return new UnionType([
|
||||
'name' => 'core_UploadAttachment',
|
||||
'types' => [
|
||||
\IPS\core\api\GraphQL\TypeRegistry::attachment(),
|
||||
\IPS\core\api\GraphQL\TypeRegistry::uploadProgress(),
|
||||
],
|
||||
'resolveType' => function ($obj) {
|
||||
if ( isset( $obj['ref'] ) )
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::uploadProgress();
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::attachment();
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this mutation
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return array
|
||||
*/
|
||||
public function resolve($val, $args, $context, $info)
|
||||
{
|
||||
$storageClass = \IPS\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( !\in_array( mb_strtolower( $ext ), array_map( 'mb_strtolower', $allowedFileTypes ) ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'FILE_TYPE_NOT_ALLOWED', '2C399/1', 403 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Chunking? */
|
||||
if ( $storageClass::$supportsChunking and isset( $args['chunk'] ) )
|
||||
{
|
||||
/* If this is the FIRST chunk, start the process */
|
||||
if ( $args['chunk'] === 1 )
|
||||
{
|
||||
$ref = $storageClass->chunkInit( $args['name'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$ref = json_decode( $args['ref'] );
|
||||
}
|
||||
|
||||
/* Process this chunk */
|
||||
$ref = $storageClass->chunkProcess( $ref, $contents, --$args['chunk'], TRUE );
|
||||
|
||||
/* If this is the LAST chunk, finish the process */
|
||||
if ( $args['chunk'] === $args['totalChunks'] )
|
||||
{
|
||||
$url = $storageClass->chunkFinish( $ref );
|
||||
$file = \IPS\File::get( 'core_Attachment', $url );
|
||||
|
||||
/* If it's got an image extension, check it's actually a valid image */
|
||||
if ( \in_array( $ext, \IPS\Image::$imageExtensions ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$file->getImageDimensions();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NOT_VALID_IMAGE', '2C399/2', 403 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're continuing to the next chunk, return an UploadProgress object containing the ref
|
||||
$ref = json_encode( $ref );
|
||||
|
||||
return array(
|
||||
'name' => $args['name'],
|
||||
'ref' => $args['ref']
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If it's got an image extension, check it's actually a valid image */
|
||||
if ( \in_array( $ext, \IPS\Image::$imageExtensions ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$image = \IPS\Image::create( $contents );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( $e->getMessage(), '2C399/2', 403 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
try
|
||||
{
|
||||
$file = \IPS\File::create( 'core_Attachment', $args['name'], $contents );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'FILE_CREATION_FAILED', '2C399/3', 403 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Make it into an attachment */
|
||||
$attachment = $file->makeAttachment( $args['postKey'], \IPS\Member::loggedIn() );
|
||||
return $attachment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: ActiveUsers query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* OurPicks query for GraphQL API
|
||||
*/
|
||||
class _ActiveUsers
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns active user information";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::activeUsers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val)
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'online', 'front' ) ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// There's no real value here, we're just passing on to the next resolver in the chain
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Club query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Club query for GraphQL API
|
||||
*/
|
||||
class _Club
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns a club";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args() {
|
||||
return array(
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::club();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member\Club
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
return \IPS\Member\Club::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Content query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Me query for GraphQL API
|
||||
*/
|
||||
class _Content
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Return a generic piece of content";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'url' => TypeRegistry::nonNull( TypeRegistry::string() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\Content\Api\GraphQL\TypeRegistry::content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
$url = \IPS\Http\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 ( $ext->classes as $class )
|
||||
{
|
||||
try
|
||||
{
|
||||
$object = $class::loadFromUrl( $url );
|
||||
|
||||
if ( isset( $url->queryString['do'] ) and $url->queryString['do'] === 'findComment' )
|
||||
{
|
||||
$commentClass = $object::$commentClass;
|
||||
$object = $commentClass::load( $url->queryString['comment'] );
|
||||
}
|
||||
elseif ( isset( $url->queryString['do'] ) and $url->queryString['do'] === 'findReview' )
|
||||
{
|
||||
$reviewClass = $object::$reviewClass;
|
||||
$object = $reviewClass::load( $url->queryString['review'] );
|
||||
}
|
||||
|
||||
if ( !$object->canView( \IPS\Member::loggedIn() ) )
|
||||
{
|
||||
// No permission
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Group query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group query for GraphQL API
|
||||
*/
|
||||
class _Group
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns a member group";
|
||||
|
||||
/*
|
||||
* @brief Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::group();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member\Group
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
return \IPS\Member\Group::load( $args['id'] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Language query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Me query for GraphQL API
|
||||
*/
|
||||
class _Language
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Return language data";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'id' => TypeRegistry::id()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::language();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
if( isset( $args['id'] ) ){
|
||||
return \IPS\Lang::load( $args['id'] );
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn()->language();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Login handler query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Oct 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login handler query for GraphQL API
|
||||
*/
|
||||
class _LoginHandlers
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Return login handler data";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::login() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public function resolve($val, $args)
|
||||
{
|
||||
$login = new \IPS\Login;
|
||||
return $login->buttonMethods();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: 'Me' query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Me query for GraphQL API
|
||||
*/
|
||||
class _Me
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Return the logged-in member";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::member();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
return \IPS\Member::loggedIn();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Member query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member query for GraphQL API
|
||||
*/
|
||||
class _Member
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Return a member";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'id' => TypeRegistry::id(),
|
||||
'loggedIn' => TypeRegistry::boolean()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::member();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
if( isset( $args['loggedIn'] ) ){
|
||||
return \IPS\Member::loggedIn();
|
||||
} elseif ( isset( $args['id'] ) ){
|
||||
return \IPS\Member::load( $args['id'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger conversation query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Messenger conversation query for GraphQL API
|
||||
*/
|
||||
class _MessengerConversation
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns a messenger conversation";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'id' => TypeRegistry::nonNull( TypeRegistry::id() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::messengerConversation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Messenger\Conversation
|
||||
*/
|
||||
public function resolve($val, $args, $context, $info)
|
||||
{
|
||||
try
|
||||
{
|
||||
$conversation = \IPS\core\Messenger\Conversation::loadAndCheckPerms( $args['id'] );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'NO_MESSAGE', '1F294/2', 400 );
|
||||
}
|
||||
|
||||
return $conversation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger conversation squery
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Messenger conversations query for GraphQL API
|
||||
*/
|
||||
class _MessengerConversations
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns a list of messenger conversations";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'folder' => TypeRegistry::id(),
|
||||
'filter' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_messengerConversations_filter',
|
||||
'values' => ['MINE', 'NOT_MINE', 'READ', 'NOT_READ']
|
||||
]),
|
||||
'defaultValue' => NULL
|
||||
],
|
||||
'search' => TypeRegistry::string(),
|
||||
'searchIn' => [
|
||||
'type' => TypeRegistry::listOf(
|
||||
TypeRegistry::eNum([
|
||||
'name' => 'core_messengerConversations_searchIn',
|
||||
'description' => 'Fields on which a conversation search can be applied',
|
||||
'values' => ['TOPIC', 'POST', 'SENDER', 'RECIPIENT']
|
||||
])
|
||||
)
|
||||
],
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
'orderBy' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_messengerConversations_orderBy',
|
||||
'description' => 'Fields on which conversations can be sorted',
|
||||
'values' => \IPS\core\api\GraphQL\Types\MessengerConversationType::getOrderByOptions()
|
||||
]),
|
||||
'defaultValue' => 'last_post_time'
|
||||
],
|
||||
'orderDir' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_messengerConversations_orderDir',
|
||||
'description' => 'Directions in which items can be sorted',
|
||||
'values' => [ 'ASC', 'DESC' ]
|
||||
]),
|
||||
'defaultValue' => 'DESC'
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerConversation() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Messenger\Conversation
|
||||
*/
|
||||
public function resolve($val, $args, $context, $info)
|
||||
{
|
||||
// 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 ) );
|
||||
$orderBy = 'mt_' . $args['orderBy'];
|
||||
$offset = max( $args['limit'], 0 );
|
||||
$limit = min( $args['limit'], 50 );
|
||||
$iterator = NULL;
|
||||
|
||||
// Filters
|
||||
if( !\is_null( $args['filter'] ) )
|
||||
{
|
||||
switch ($args['filter'] ) {
|
||||
case "MINE":
|
||||
$where[] = array( 'core_message_topic_user_map.map_is_starter=1' );
|
||||
break;
|
||||
case "NOT_MINE":
|
||||
$where[] = array( 'core_message_topic_user_map.map_is_starter=0' );
|
||||
break;
|
||||
case "READ":
|
||||
$where[] = array( 'core_message_topic_user_map.map_has_unread=0' );
|
||||
break;
|
||||
case "NOT_READ":
|
||||
$where[] = array( 'core_message_topic_user_map.map_has_unread=1' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Folder
|
||||
if ( !\is_null( $args['folder'] ) )
|
||||
{
|
||||
$folderObj = \IPS\core\api\GraphQL\TypeRegistry::messengerFolder();
|
||||
$folders = $folderObj->getMemberFolders();
|
||||
|
||||
if( isset( $folders[ $args['folder'] ] ) )
|
||||
{
|
||||
$where[] = array( 'core_message_topic_user_map.map_folder_id=?', $args['folder'] );
|
||||
}
|
||||
}
|
||||
|
||||
// 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 );
|
||||
$where = array( array( 'core_message_posts.msg_topic_id IN (?)', $subQuery ) );
|
||||
$query = array();
|
||||
$prefix = \IPS\Db::i()->prefix;
|
||||
|
||||
if ( !\is_null( $args['searchIn'] ) )
|
||||
{
|
||||
if ( \in_array( 'TOPIC', $args['searchIn'] ) )
|
||||
{
|
||||
$query[] = \IPS\Content\Search\Mysql\Query::matchClause( "core_message_topics.mt_title", $args['search'], '+', FALSE );
|
||||
}
|
||||
if ( \in_array( 'POST', $args['searchIn'] ) )
|
||||
{
|
||||
$query[] = \IPS\Content\Search\Mysql\Query::matchClause( "core_message_posts.msg_post", $args['search'], '+', FALSE );
|
||||
}
|
||||
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'] ) . "%' ) )";
|
||||
}
|
||||
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'] ) . "%') )";
|
||||
}
|
||||
|
||||
if ( \count( $query ) )
|
||||
{
|
||||
$where[] = array( '(' . implode( ' OR ', $query ) . ')' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a count */
|
||||
try
|
||||
{
|
||||
$count = \IPS\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'
|
||||
)
|
||||
->join(
|
||||
'core_message_topic_user_map',
|
||||
'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id'
|
||||
)
|
||||
->first();
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
$count = 0;
|
||||
}
|
||||
|
||||
/* Performance: if count is 0, don't bother selecting ... it's a wasted query */
|
||||
if( $count )
|
||||
{
|
||||
/* Because of strict group by, we first need to select the ids, then grab those topics */
|
||||
$iterator = \IPS\Db::i()->select(
|
||||
'core_message_posts.msg_topic_id',
|
||||
'core_message_posts',
|
||||
$where,
|
||||
$orderBy . ' ' . $args['orderDir'],
|
||||
array( $offset, $limit ),
|
||||
array( 'msg_topic_id', $orderBy )
|
||||
)->join(
|
||||
'core_message_topics',
|
||||
'core_message_posts.msg_topic_id=core_message_topics.mt_id'
|
||||
)->join(
|
||||
'core_message_topic_user_map',
|
||||
'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id'
|
||||
);
|
||||
|
||||
/* Get iterator */
|
||||
$iterator = \IPS\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 ) ) . ')' ),
|
||||
$orderBy . ' ' . $args['orderDir']
|
||||
)->join(
|
||||
'core_message_topics',
|
||||
'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get iterator */
|
||||
$iterator = \IPS\Db::i()->select(
|
||||
'core_message_topic_user_map.*, core_message_topics.*',
|
||||
'core_message_topic_user_map',
|
||||
$where,
|
||||
$orderBy . ' ' . $args['orderDir'],
|
||||
array( $offset, $limit )
|
||||
)->join(
|
||||
'core_message_topics',
|
||||
'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id'
|
||||
);
|
||||
}
|
||||
|
||||
if( $iterator )
|
||||
{
|
||||
return new \IPS\Patterns\ActiveRecordIterator( $iterator, 'IPS\core\Messenger\Conversation' );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger conversation query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Messenger folders query for GraphQL API
|
||||
*/
|
||||
class _MessengerFolders
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns the member's messenger folders";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerFolder() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Messenger\Conversation
|
||||
*/
|
||||
public function resolve($val, $args, $context, $info)
|
||||
{
|
||||
if( !\IPS\Member::loggedIn()->member_id || \IPS\Member::loggedIn()->members_disable_pm )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$folderObj = \IPS\core\api\GraphQL\TypeRegistry::messengerFolder();
|
||||
return $folderObj->getMemberFolders();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Mobile menu query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 12 June 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile menu query for GraphQL API
|
||||
*/
|
||||
class _MobileMenu
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns details of the mobile app menu";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::mobileMenu() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return array
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
$return = array();
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_mobileapp_menu', NULL, 'position' ) as $item )
|
||||
{
|
||||
if ( \IPS\Application::appIsEnabled( $item['app'] ) )
|
||||
{
|
||||
$class = 'IPS\\' . $item['app'] . '\extensions\core\MobileNavigation\\' . $item['extension'];
|
||||
if ( class_exists( $class ) )
|
||||
{
|
||||
$object = new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'] );
|
||||
if ( $object->canView() )
|
||||
{
|
||||
$return[ $item['id'] ] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Notification types query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Jan 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationTypes query for GraphQL API
|
||||
*/
|
||||
class _NotificationTypes
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns available notification types";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::notificationType() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
//$defaultConfiguration = \IPS\Notification::defaultConfiguration();
|
||||
//$memberConfiguration = $member->notificationsConfiguration();
|
||||
$notificationTypes = array();
|
||||
|
||||
$extensions = \IPS\Application::allExtensions( 'core', 'Notifications' );
|
||||
$availableOptions = array();
|
||||
|
||||
foreach( $extensions as $extensionKey => $extension )
|
||||
{
|
||||
$extensionOptions = \IPS\Notification::availableOptions( \IPS\Member::loggedIn(), $extension );
|
||||
|
||||
foreach( $extensionOptions as $key => $option )
|
||||
{
|
||||
// For now, only return 'standard' types, which are the email/inline/push options
|
||||
if( $option['type'] !== 'standard' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$methods = array('inline' => array(), 'email' => array(), 'push' => array());
|
||||
|
||||
foreach( $methods as $method => $methodData )
|
||||
{
|
||||
if( !isset( $option['options'][ $method ] ) )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
$notificationTypes[] = array(
|
||||
'id' => $extensionKey === $key ? $extensionKey : $extensionKey . '_' . $key,
|
||||
'extension' => $extensionKey,
|
||||
'type' => $key,
|
||||
'name' => $option['title'],
|
||||
'description' => $option['description'],
|
||||
'inline' => $methods['inline'],
|
||||
'email' => $methods['email'],
|
||||
'push' => $methods['push']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $notificationTypes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: OurPicks query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* OurPicks query for GraphQL API
|
||||
*/
|
||||
class _OurPicks
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns promoted items";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'count' => TypeRegistry::int()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::promotedItem() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
$limit = 7;
|
||||
if( isset( $args['count'] ) && \is_int( $args['count'] ) )
|
||||
{
|
||||
$limit = $args['count'];
|
||||
}
|
||||
|
||||
$items = \IPS\core\Promote::internalStream( $limit );
|
||||
if( !\count( $items ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: PopularContributors query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 11 Feb 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PopularContributors query for GraphQL API
|
||||
*/
|
||||
class _PopularContributors
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns popular contributors";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 5
|
||||
],
|
||||
'period' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_PopularContributors_period',
|
||||
'values' => ['WEEK', 'MONTH', 'YEAR', 'ALL']
|
||||
]),
|
||||
'defaultValue' => 'WEEK'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::popularContributor() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
/* How many? */
|
||||
$limit = min( $args['limit'], 25 );
|
||||
|
||||
/* What timeframe? */
|
||||
$where = array( array( 'member_received > 0' ) );
|
||||
$timeframe = 'all';
|
||||
if ( $args['period'] !== 'ALL' )
|
||||
{
|
||||
switch ( $args['period'] )
|
||||
{
|
||||
case 'WEEK':
|
||||
$where[] = array( 'rep_date>' . \IPS\DateTime::create()->sub( new \DateInterval( 'P1W' ) )->getTimestamp() );
|
||||
break;
|
||||
case 'MONTH':
|
||||
$where[] = array( 'rep_date>' . \IPS\DateTime::create()->sub( new \DateInterval( 'P1M' ) )->getTimestamp() );
|
||||
break;
|
||||
case 'YEAR':
|
||||
$where[] = array( 'rep_date>' . \IPS\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') );
|
||||
}
|
||||
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') );
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
\IPS\Member::constructFromData( $member );
|
||||
}
|
||||
|
||||
$output = array();
|
||||
|
||||
foreach ( $topContributors as $memberID => $rep )
|
||||
{
|
||||
$output[] = array(
|
||||
'rep' => $rep,
|
||||
'member_id' => $memberID
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Search query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 22 Sep 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search query for GraphQL API
|
||||
*/
|
||||
class _Search
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns search results";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'term' => TypeRegistry::string(),
|
||||
'type' => [
|
||||
'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() ) )
|
||||
]),
|
||||
],
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
'orderBy' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_search_order_by',
|
||||
'description' => 'Fields on which reuslts can be sorted',
|
||||
'values' => [ 'newest', 'relevancy', 'joined', 'name', 'member_posts', 'pp_reputation_points' ]
|
||||
])
|
||||
],
|
||||
'orderDir' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_search_order_dir',
|
||||
'description' => 'Directions in which reuslts can be sorted',
|
||||
'values' => [ 'ASC', 'DESC' ]
|
||||
]),
|
||||
'defaultValue' => 'DESC'
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::search();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
$where = array();
|
||||
$returnObject = array();
|
||||
$offset = max( $args['offset'], 0 );
|
||||
$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 ( $args['term'] )
|
||||
{
|
||||
$where = array( array( 'LOWER(core_members.name) LIKE ?', '%' . mb_strtolower( trim( $args['term'] ) ) . '%' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where = array( array( 'core_members.name<>?', '' ) );
|
||||
}
|
||||
|
||||
// Ordering
|
||||
$orderDir = isset( $args['orderDir'] ) ? $args['orderDir'] : 'DESC';
|
||||
$orderBy = 'name';
|
||||
|
||||
if( isset( $args['orderBy'] ) && \in_array( $args['orderBy'], array( 'joined', 'name', 'member_posts', 'pp_reputation_points' ) ) )
|
||||
{
|
||||
$orderBy = $args['orderBy'];
|
||||
}
|
||||
|
||||
$order = $orderBy . ' ' . $orderDir;
|
||||
|
||||
// Get results
|
||||
$select = \IPS\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->join( 'core_pfields_content', 'core_pfields_content.member_id=core_members.member_id' );
|
||||
|
||||
$returnObject['results'] = new \IPS\Patterns\ActiveRecordIterator( $select, 'IPS\Member' );
|
||||
|
||||
return $returnObject;
|
||||
}
|
||||
|
||||
// Content search
|
||||
$query = \IPS\Content\Search\Query::init();
|
||||
$types = \IPS\core\modules\front\search\search::contentTypes();
|
||||
|
||||
if( isset( $args['type'] ) && !empty( $args['type'] ) )
|
||||
{
|
||||
$class = $types[ $args['type'] ];
|
||||
$filter = \IPS\Content\Search\ContentFilter::init( $class );
|
||||
$query->filterByContent( array( $filter ) );
|
||||
}
|
||||
|
||||
$orderBy = $query->getDefaultSortMethod();
|
||||
|
||||
// Ordering
|
||||
if( isset( $args['orderBy'] ) )
|
||||
{
|
||||
switch( $args['orderBy'] )
|
||||
{
|
||||
case 'newest':
|
||||
$query->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_CREATED );
|
||||
break;
|
||||
case 'relevancy':
|
||||
$query->setOrder( \IPS\Content\Search\Query::ORDER_RELEVANCY );
|
||||
break;
|
||||
default:
|
||||
$query->setOrder( $orderBy );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$query->setOrder( $orderBy );
|
||||
}
|
||||
|
||||
// Get page
|
||||
// We don't know the count at this stage, so figure out the page number from
|
||||
// our offset/limit
|
||||
$page = 1;
|
||||
|
||||
if( $offset > 0 )
|
||||
{
|
||||
$page = floor( $offset / $limit ) + 1;
|
||||
}
|
||||
|
||||
$query->setLimit( $limit )->setPage( $page );
|
||||
|
||||
/* Run query */
|
||||
$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,
|
||||
NULL
|
||||
);
|
||||
$returnObject['count'] = $returnObject['results']->count( TRUE );
|
||||
|
||||
return $returnObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Settings query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings query for GraphQL API
|
||||
*/
|
||||
class _Settings
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns values of system settings (subject to the setting being exposed by the API)";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\Settings
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
return \IPS\Settings::i();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Stream query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream query for GraphQL API
|
||||
*/
|
||||
class _Stream
|
||||
{
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns an activity stream";
|
||||
|
||||
/*
|
||||
* Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array(
|
||||
'id' => TypeRegistry::id()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return \IPS\core\Stream
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
if( isset( $args['id'] ) && \intval( $args['id'] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$stream = \IPS\core\Stream::load( $args['id'] );
|
||||
}
|
||||
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' ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_STREAM', 'GQL/0003/1', 403 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start with a blank stream */
|
||||
$stream = \IPS\core\Stream::allActivityStream();
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Streams query
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 10 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Queries;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams query for GraphQL API
|
||||
*/
|
||||
class _Streams
|
||||
{
|
||||
|
||||
/*
|
||||
* @brief Query description
|
||||
*/
|
||||
public static $description = "Returns a list of user's activity streams";
|
||||
|
||||
/*
|
||||
* @brief Query arguments
|
||||
*/
|
||||
public function args()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query return type
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::stream() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves this query
|
||||
*
|
||||
* @param mixed Value passed into this resolver
|
||||
* @param array Arguments
|
||||
* @param array Context values
|
||||
* @return array
|
||||
*/
|
||||
public function resolve($val, $args, $context)
|
||||
{
|
||||
$streams = array();
|
||||
|
||||
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\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' ) ) )
|
||||
{
|
||||
$streams[ $stream->id ] = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\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 )
|
||||
{
|
||||
$streams[ $stream->id ] = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
return $streams;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Core controller
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core controller for GraphQL API
|
||||
* @todo maybe this shouldn't be a class since it only has a static method?
|
||||
*/
|
||||
abstract class _Query
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the supported query types in this app
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function queries()
|
||||
{
|
||||
return [
|
||||
'activeUsers' => new \IPS\core\api\GraphQL\Queries\ActiveUsers(),
|
||||
'club' => new \IPS\core\api\GraphQL\Queries\Club(),
|
||||
'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(),
|
||||
'messengerConversation' => new \IPS\core\api\GraphQL\Queries\MessengerConversation(),
|
||||
'messengerConversations' => new \IPS\core\api\GraphQL\Queries\MessengerConversations(),
|
||||
'messengerFolders' => new \IPS\core\api\GraphQL\Queries\MessengerFolders(),
|
||||
'mobileMenu' => new \IPS\core\api\GraphQL\Queries\MobileMenu(),
|
||||
'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(),
|
||||
'stream' => new \IPS\core\api\GraphQL\Queries\Stream(),
|
||||
'streams' => new \IPS\core\api\GraphQL\Queries\Streams(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Core type registry
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\Types;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core type registry. GraphQL requires exactly one instance of each type,
|
||||
* so we'll generate singletons here.
|
||||
* @todo automate this somehow?
|
||||
*/
|
||||
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 $mobileMenu;
|
||||
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 $stream;
|
||||
protected static $tag;
|
||||
protected static $settings;
|
||||
protected static $uploadProgress;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// Defined to suppress static warnings
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ActiveUsersType
|
||||
*/
|
||||
public static function activeUsers()
|
||||
{
|
||||
return self::$activeUsers ?: (self::$activeUsers = new \IPS\core\api\GraphQL\Types\ActiveUsersType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ActiveUsersType
|
||||
*/
|
||||
public static function activeUser()
|
||||
{
|
||||
return self::$activeUser ?: (self::$activeUser = new \IPS\core\api\GraphQL\Types\ActiveUserType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AttachmentType
|
||||
*/
|
||||
public static function attachment()
|
||||
{
|
||||
return self::$attachment ?: (self::$attachment = new \IPS\core\api\GraphQL\Types\AttachmentType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AttachmentPermissionsType
|
||||
*/
|
||||
public static function attachmentPermissions()
|
||||
{
|
||||
return self::$attachmentPermissions ?: (self::$attachmentPermissions = new \IPS\core\api\GraphQL\Types\AttachmentPermissionsType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClubNodeType
|
||||
*/
|
||||
public static function clubNode()
|
||||
{
|
||||
return self::$clubNode ?: (self::$clubNode = new \IPS\core\api\GraphQL\Types\ClubNodeType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClubNodeType
|
||||
*/
|
||||
public static function club()
|
||||
{
|
||||
return self::$club ?: (self::$club = new \IPS\core\api\GraphQL\Types\ClubType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContentReactionType
|
||||
*/
|
||||
public static function contentReaction()
|
||||
{
|
||||
return self::$contentReaction ?: (self::$contentReaction = new \IPS\core\api\GraphQL\Types\ContentReactionType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GroupType
|
||||
*/
|
||||
public static function group()
|
||||
{
|
||||
return self::$group ?: (self::$group = new \IPS\core\api\GraphQL\Types\GroupType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IgnoreOptionType
|
||||
*/
|
||||
public static function ignoreOption()
|
||||
{
|
||||
return self::$ignoreOption ?: (self::$ignoreOption = new \IPS\core\api\GraphQL\Types\IgnoreOptionType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MemberType
|
||||
*/
|
||||
public static function member()
|
||||
{
|
||||
return self::$member ?: (self::$member = new \IPS\core\api\GraphQL\Types\MemberType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MobileMenuType
|
||||
*/
|
||||
public static function mobileMenu()
|
||||
{
|
||||
return self::$mobileMenu ?: (self::$mobileMenu = new \IPS\core\api\GraphQL\Types\MobileMenuType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageType
|
||||
*/
|
||||
public static function language()
|
||||
{
|
||||
return self::$language ?: (self::$language = new \IPS\core\api\GraphQL\Types\LanguageType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoginType
|
||||
*/
|
||||
public static function login()
|
||||
{
|
||||
return self::$login ?: (self::$login = new \IPS\core\api\GraphQL\Types\LoginType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessengerConversationType
|
||||
*/
|
||||
public static function messengerConversation()
|
||||
{
|
||||
return self::$messengerConversation ?: (self::$messengerConversation = new \IPS\core\api\GraphQL\Types\MessengerConversationType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessengerFolderType
|
||||
*/
|
||||
public static function messengerFolder()
|
||||
{
|
||||
return self::$messengerFolder ?: (self::$messengerFolder = new \IPS\core\api\GraphQL\Types\MessengerFolderType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessengerParticipantType
|
||||
*/
|
||||
public static function messengerParticipant()
|
||||
{
|
||||
return self::$messengerParticipant ?: (self::$messengerParticipant = new \IPS\core\api\GraphQL\Types\MessengerParticipantType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessengerReplyType
|
||||
*/
|
||||
public static function messengerReply()
|
||||
{
|
||||
return self::$messengerReply ?: (self::$messengerReply = new \IPS\core\api\GraphQL\Types\MessengerReplyType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotificationType
|
||||
*/
|
||||
public static function notification()
|
||||
{
|
||||
return self::$notification ?: (self::$notification = new \IPS\core\api\GraphQL\Types\NotificationType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotificationMethod
|
||||
*/
|
||||
public static function notificationMethod()
|
||||
{
|
||||
return self::$notificationMethod ?: (self::$notificationMethod = new \IPS\core\api\GraphQL\Types\NotificationMethodType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotificationTypeType
|
||||
*/
|
||||
public static function notificationType()
|
||||
{
|
||||
return self::$notificationType ?: (self::$notificationType = new \IPS\core\api\GraphQL\Types\NotificationTypeType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PollType
|
||||
*/
|
||||
public static function poll()
|
||||
{
|
||||
return self::$poll ?: (self::$poll = new \IPS\core\api\GraphQL\Types\PollType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PollQuestionType
|
||||
*/
|
||||
public static function pollQuestion()
|
||||
{
|
||||
return self::$pollQuestion ?: (self::$pollQuestion = new \IPS\core\api\GraphQL\Types\PollQuestionType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PopularContributorType
|
||||
*/
|
||||
public static function popularContributor()
|
||||
{
|
||||
return self::$popularContributor ?: (self::$popularContributor = new \IPS\core\api\GraphQL\Types\PopularContributorType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProfileFieldGroupType
|
||||
*/
|
||||
public static function profileFieldGroup()
|
||||
{
|
||||
return self::$profileFieldGroup ?: (self::$profileFieldGroup = new \IPS\core\api\GraphQL\Types\ProfileFieldGroupType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProfileFieldType
|
||||
*/
|
||||
public static function profileField()
|
||||
{
|
||||
return self::$profileField ?: (self::$profileField = new \IPS\core\api\GraphQL\Types\ProfileFieldType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PromotedItemType
|
||||
*/
|
||||
public static function promotedItem()
|
||||
{
|
||||
return self::$promotedItem ?: (self::$promotedItem = new \IPS\core\api\GraphQL\Types\PromotedItemType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContentSearchResultType
|
||||
*/
|
||||
public static function contentSearchResult()
|
||||
{
|
||||
return self::$contentSearchResult ?: (self::$contentSearchResult = new \IPS\core\api\GraphQL\Types\ContentSearchResultType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReportType
|
||||
*/
|
||||
public static function report()
|
||||
{
|
||||
return self::$report ?: (self::$report = new \IPS\core\api\GraphQL\Types\ReportType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReportReasonType
|
||||
*/
|
||||
public static function reportReason()
|
||||
{
|
||||
return self::$reportReason ?: (self::$reportReason = new \IPS\core\api\GraphQL\Types\ReportReasonType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SearchResultType
|
||||
*/
|
||||
public static function searchResult()
|
||||
{
|
||||
return self::$searchResult ?: (self::$searchResult = new \IPS\core\api\GraphQL\Types\SearchResultType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SearchType
|
||||
*/
|
||||
public static function search()
|
||||
{
|
||||
return self::$search ?: (self::$search = new \IPS\core\api\GraphQL\Types\SearchType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StreamType
|
||||
*/
|
||||
public static function stream()
|
||||
{
|
||||
return self::$stream ?: (self::$stream = new \IPS\core\api\GraphQL\Types\StreamType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TagType
|
||||
*/
|
||||
public static function tag()
|
||||
{
|
||||
return self::$tag ?: (self::$tag = new \IPS\core\api\GraphQL\Types\TagType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SettingsType
|
||||
*/
|
||||
public static function settings()
|
||||
{
|
||||
return self::$settings ?: (self::$settings = new \IPS\core\api\GraphQL\Types\SettingsType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UploadProgressType
|
||||
*/
|
||||
public static function uploadProgress()
|
||||
{
|
||||
return self::$uploadProgress ?: (self::$uploadProgress = new \IPS\core\api\GraphQL\Types\UploadProgressType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: ActiveUsers Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ActiveUsers for GraphQL API
|
||||
*/
|
||||
class _ActiveUserType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ActiveUser',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'resolve' => function ($user, $args) {
|
||||
return \IPS\Http\Url::createFromString( $user['location_url'] );
|
||||
}
|
||||
],
|
||||
'lang' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'args' => [
|
||||
'action' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => TRUE
|
||||
]
|
||||
],
|
||||
'resolve' => function ($user, $args) {
|
||||
return self::getLocationLang( $user, $args['action'] );
|
||||
}
|
||||
],
|
||||
'anonymous' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'resolve' => function ($user, $args) {
|
||||
return $user['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS;
|
||||
}
|
||||
],
|
||||
'ipAddress' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'resolve' => function ($user, $args) {
|
||||
if( \IPS\Member::loggedIn()->modPermission( 'can_use_ip_tools' ) )
|
||||
{
|
||||
return $user['ip_address'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'timestamp' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'resolve' => function ($user, $args) {
|
||||
return $user['running_time'];
|
||||
}
|
||||
],
|
||||
'user' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'resolve' => function ($user, $args) {
|
||||
if( $user['member_id'] )
|
||||
{
|
||||
return \IPS\Member::load( $user['member_id'] );
|
||||
}
|
||||
|
||||
return new \IPS\Member;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the language string showing what the user is doing
|
||||
* @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
|
||||
*/
|
||||
protected static function getLocationLang($user, $action)
|
||||
{
|
||||
try
|
||||
{
|
||||
if( !$user['location_lang'] )
|
||||
{
|
||||
return \IPS\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 ) )
|
||||
{
|
||||
$sprintf = array();
|
||||
$data = json_decode( $user['location_data'], TRUE );
|
||||
|
||||
if ( !empty( $data ) )
|
||||
{
|
||||
foreach ( $data as $key => $parse )
|
||||
{
|
||||
$value = htmlspecialchars( $parse ? \IPS\Member::loggedIn()->language()->get( $key ) : $key, ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
$sprintf[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if( $action && \IPS\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 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( $user['location_lang'], FALSE, array( 'sprintf' => $sprintf ) );
|
||||
}
|
||||
|
||||
return sprintf( $lang, $sprintf );
|
||||
}
|
||||
}
|
||||
catch ( \UnderflowException $e ){
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: ActiveUsers Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ActiveUsers for GraphQL API
|
||||
*/
|
||||
class _ActiveUsersType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ActiveUsers',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'count' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'args' => [
|
||||
'includeGuests' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => FALSE
|
||||
]
|
||||
],
|
||||
'resolve' => function ($val, $args) {
|
||||
$flags = \IPS\Session\Store::ONLINE_MEMBERS;
|
||||
|
||||
if( $args['includeGuests'] )
|
||||
{
|
||||
$flags = \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_GUESTS;
|
||||
}
|
||||
|
||||
return \IPS\Session\Store::i()->getOnlineUsers( $flags | \IPS\Session\Store::ONLINE_COUNT_ONLY, 'asc', NULL, NULL, \IPS\Member::loggedIn()->isAdmin() );
|
||||
}
|
||||
],
|
||||
'users' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::activeUser() ),
|
||||
'args' => [
|
||||
'includeGuests' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => FALSE
|
||||
],
|
||||
'sortDir' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'activeusers_sort_dir',
|
||||
'values' => ['asc', 'desc']
|
||||
]),
|
||||
'defaultValue' => 'desc'
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
]
|
||||
],
|
||||
'resolve' => function ($val, $args) {
|
||||
$flags = \IPS\Session\Store::ONLINE_MEMBERS;
|
||||
|
||||
if( $args['includeGuests'] )
|
||||
{
|
||||
$flags = \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\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;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Attachment Permissions Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 28 May 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* AttachmentPermissionsType for GraphQL API
|
||||
*/
|
||||
class _AttachmentPermissionsType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_AttachmentPermissions',
|
||||
'description' => 'Details about what the user can attach',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'consumedUploadSize' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "The maximum *combined* size (in bytes) remaining for attachments, or NULL for no restriction. Note: if 0 this means no more uploads are allowed. If the user deletes an attachment, you should query again for an updated value.",
|
||||
'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 )
|
||||
{
|
||||
$currentPostUsage += $attachment['attach_filesize'];
|
||||
}
|
||||
return $currentPostUsage;
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Attachment Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 22 May 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* AttachmentType for GraphQL API
|
||||
*/
|
||||
class _AttachmentType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Attachment',
|
||||
'description' => 'An attachment',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "The attachment ID",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['attach_id'];
|
||||
}
|
||||
],
|
||||
'ext' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The attachment's filename extension",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['attach_ext'];
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The attachment's original filename (not necessarily what is actually stored on disk)",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['attach_file'];
|
||||
}
|
||||
],
|
||||
'size' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Size of attachment in bytes",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['attach_filesize'];
|
||||
}
|
||||
],
|
||||
'image' => [
|
||||
'type' => TypeRegistry::image(),
|
||||
'description' => "Image details (will be NULL for non-image files)",
|
||||
'resolve' => function( $attachment ) {
|
||||
if ( $attachment['attach_is_image'] )
|
||||
{
|
||||
return array(
|
||||
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->url,
|
||||
'width' => $attachment['attach_img_width'],
|
||||
'height' => $attachment['attach_img_height'],
|
||||
);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'thumbnail' => [
|
||||
'type' => TypeRegistry::image(),
|
||||
'description' => "A thumbnail for the attachment (will be NULL for non-image files)",
|
||||
'resolve' => function( $attachment ) {
|
||||
if ( $attachment['attach_is_image'] )
|
||||
{
|
||||
if ( $attachment['attach_thumb_location'] )
|
||||
{
|
||||
return array(
|
||||
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->url,
|
||||
'width' => $attachment['attach_thumb_width'],
|
||||
'height' => $attachment['attach_thumb_height'],
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return array(
|
||||
'url' => (string) \IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->url,
|
||||
'width' => $attachment['attach_img_width'],
|
||||
'height' => $attachment['attach_img_height'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'date' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of when the attachment was uploaded",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['attach_date'];
|
||||
}
|
||||
],
|
||||
'uploader' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => "Member who uploaded the attachment",
|
||||
'resolve' => function ( $attachment ) {
|
||||
return \IPS\Member::load( $attachment['attach_member_id'] );
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Club Node Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ClubNodeType for GraphQL API
|
||||
*/
|
||||
class _ClubNodeType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ClubNode',
|
||||
'description' => 'Club Nodes',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Node ID",
|
||||
'resolve' => function ($node, $args, $context, $info) {
|
||||
return $node['node_id'];
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Node name",
|
||||
'resolve' => function ($node, $args, $context, $info) {
|
||||
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'] );
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Node type (from classname)",
|
||||
'resolve' => function ($node, $args, $context, $info) {
|
||||
return str_replace('\\', '_', str_replace('IPS\\', '', $node['node_class'] ) );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Club Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ClubType for GraphQL API
|
||||
*/
|
||||
class _ClubType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Club',
|
||||
'description' => 'Clubs',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Club ID",
|
||||
'resolve' => function ($club) {
|
||||
return $club->id;
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club name",
|
||||
'resolve' => function ($club) {
|
||||
return $club->name;
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club type",
|
||||
'resolve' => function ($club) {
|
||||
return $club->type;
|
||||
}
|
||||
],
|
||||
'seoTitle' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club SEO title",
|
||||
'resolve' => function ($club) {
|
||||
return \IPS\Http\Url\Friendly::seoTitle( $club->name );
|
||||
}
|
||||
],
|
||||
'createdDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of club creation date",
|
||||
'resolve' => function ($club) {
|
||||
return $club->created;
|
||||
}
|
||||
],
|
||||
'memberCount' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Number of members in this club",
|
||||
'resolve' => function ($club) {
|
||||
return $club->members;
|
||||
}
|
||||
],
|
||||
'members' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::member() ),
|
||||
'description' => "List of members in this club",
|
||||
'args' => [
|
||||
'type' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'club_member_type',
|
||||
'values' => ['all', 'leader', 'moderator', 'member']
|
||||
]),
|
||||
'defaultValue' => 'all'
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
]
|
||||
],
|
||||
'resolve' => function ($club, $args) {
|
||||
return self::members( $club, $args );
|
||||
}
|
||||
],
|
||||
'owner' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => "Owner of this club",
|
||||
'resolve' => function ($club) {
|
||||
return $club->owner;
|
||||
}
|
||||
],
|
||||
'icon' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club icon image",
|
||||
'resolve' => function ($club) {
|
||||
return ( $club->profile_photo ) ? \IPS\File::get( 'core_Clubs', $club->profile_photo )->url : null;
|
||||
}
|
||||
],
|
||||
'coverPhoto' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club cover photo image",
|
||||
'resolve' => function ($club) {
|
||||
return ( $club->coverPhoto(FALSE)->file ) ? $club->coverPhoto(FALSE)->file : null;
|
||||
}
|
||||
],
|
||||
'isFeatured' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Club featured flag",
|
||||
'resolve' => function ($club) {
|
||||
return $club->featured;
|
||||
}
|
||||
],
|
||||
'about' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Club description",
|
||||
'resolve' => function ($club) {
|
||||
return $club->about;
|
||||
}
|
||||
],
|
||||
'lastActivity' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of the last activity in this club",
|
||||
'resolve' => function ($club) {
|
||||
return $club->last_activity;
|
||||
}
|
||||
],
|
||||
'nodes' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::clubNode() ),
|
||||
'description' => "List of nodes in this club",
|
||||
'resolve' => function ($club, $args) {
|
||||
return self::nodes( $club, $args );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve nodes field
|
||||
*
|
||||
* @param \IPS\Member\Club
|
||||
* @param array Arguments passed to this resolver
|
||||
* @return array
|
||||
*/
|
||||
protected static function nodes($club, $args)
|
||||
{
|
||||
return $club->nodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve members field
|
||||
*
|
||||
* @param \IPS\Member\Club
|
||||
* @param array Arguments passed to this resolver
|
||||
* @return array
|
||||
*/
|
||||
protected static function members($club, $args)
|
||||
{
|
||||
$memberType = ( $args['type'] == 'all' ) ? array( 'member', 'moderator', 'leader' ) : array( $args['type'] );
|
||||
$result = array();
|
||||
$limit = min( $args['limit'], 50 );
|
||||
$members = $club->members( $memberType, $limit, 'core_clubs_memberships.joined ASC', 2 );
|
||||
|
||||
foreach( $members as $memberRow ){
|
||||
$result[] = \IPS\Member::constructFromData( $memberRow );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Content Reactions Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ContentReactionType for GraphQL API
|
||||
*/
|
||||
class _ContentReactionType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Reaction',
|
||||
'description' => 'Content reactions',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Reaction key. If this is a reaction given to content, this will be a unique key. If this is representing a particular reaction as a whole, it will be that reaction's ID.",
|
||||
'resolve' => function ($reaction) {
|
||||
return isset( $reaction['reaction'] ) ? $reaction['reaction']->id : $reaction['id'];
|
||||
}
|
||||
],
|
||||
'reactionId' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "The reaction's actual ID",
|
||||
'resolve' => function ($reaction) {
|
||||
return isset( $reaction['reaction'] ) ? $reaction['reaction']->id : $reaction['reactionId'];
|
||||
}
|
||||
],
|
||||
'count' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Count of this reaction type",
|
||||
'resolve' => function ($reaction) {
|
||||
return isset( $reaction['count'] ) ? $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'] );
|
||||
return (string) $_reaction->_icon->url;
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'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));
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Search result Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ContentSearchResultType for GraphQL API
|
||||
*/
|
||||
class _ContentSearchResultType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ContentSearchResult',
|
||||
'description' => 'A search result item',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'indexID' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Index ID of this result",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_id', $result);
|
||||
}
|
||||
],
|
||||
'itemID' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "ID of this result item",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_item_id', $result);
|
||||
}
|
||||
],
|
||||
'objectID' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "ID of the indexed result",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_object_id', $result);
|
||||
}
|
||||
],
|
||||
'containerID' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "ID of this result container",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_container_id', $result);
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'description' => "URL to this result item",
|
||||
'resolve' => function ($result) {
|
||||
return self::url($result);
|
||||
}
|
||||
],
|
||||
'containerTitle' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Title of this result container",
|
||||
'resolve' => function ($result) {
|
||||
return self::containerTitle( $result );
|
||||
}
|
||||
],
|
||||
'class' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Class of this result item",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_class', $result);
|
||||
}
|
||||
],
|
||||
'itemClass' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'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;
|
||||
}
|
||||
],
|
||||
// @todo this should really be in a more generic ClassType that returns
|
||||
// data about each class, with appropriate permission checking in place
|
||||
'firstCommentRequired' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Does the result class require a first comment (e.g. topics)?",
|
||||
'resolve' => function ($result) {
|
||||
$asArray = $result->asArray();
|
||||
$itemClass = static::getItemClass( $asArray );
|
||||
|
||||
return $itemClass::$firstCommentRequired;
|
||||
}
|
||||
],
|
||||
'content' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Search result content",
|
||||
'args' => [
|
||||
'truncateLength' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => 'Characters to truncate on.',
|
||||
'defaultValue' => 0
|
||||
]
|
||||
],
|
||||
'resolve' => function ($result, $args) {
|
||||
$string = html_entity_decode( self::getFieldValue('index_content', $result), ENT_QUOTES, 'UTF-8' );
|
||||
$string = preg_replace( "/\r|\n|\t/", '', $string );
|
||||
|
||||
/* Truncate */
|
||||
if( $args['truncateLength'] )
|
||||
{
|
||||
$string = mb_substr( $string, 0, $args['truncateLength'] );
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
],
|
||||
'contentImages' => [
|
||||
'type' => TypeRegistry::listOf( TypeRegistry::string() ),
|
||||
'description' => "Item images for this search result",
|
||||
'resolve' => function ($result) {
|
||||
return self::contentImages( $result );
|
||||
}
|
||||
],
|
||||
'articleLang' => [
|
||||
'type' => new ObjectType([
|
||||
'name' => 'core_articleLang',
|
||||
'fields' => [
|
||||
'indefinite' => TypeRegistry::string(),
|
||||
'definite' => TypeRegistry::string(),
|
||||
'definiteUC' => TypeRegistry::string()
|
||||
],
|
||||
'resolveField' => function ($result, $args, $context, $info) {
|
||||
$asArray = $result->asArray();
|
||||
$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;
|
||||
}
|
||||
}
|
||||
]),
|
||||
'resolve' => function ($result) {
|
||||
return $result;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Search result item title",
|
||||
'resolve' => function ($result) {
|
||||
return self::title($result);
|
||||
}
|
||||
],
|
||||
'unread' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'resolve' => function ($result) {
|
||||
return self::isUnread( $result );
|
||||
}
|
||||
],
|
||||
'hiddenStatus' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'search_hiddenStatus',
|
||||
'values' => ['HIDDEN', 'PENDING', 'DELETED']
|
||||
]),
|
||||
'resolve' => function ($result) {
|
||||
switch( self::getFieldValue('index_hidden', $result) ){
|
||||
case -2:
|
||||
return 'DELETED';
|
||||
case -1:
|
||||
return 'HIDDEN';
|
||||
case 1:
|
||||
return 'PENDING';
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
],
|
||||
'updated' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of when this result was updated",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_date_updated', $result);
|
||||
}
|
||||
],
|
||||
'created' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of when this result was created",
|
||||
'resolve' => function ($result) {
|
||||
return self::getFieldValue('index_date_created', $result);
|
||||
}
|
||||
],
|
||||
'isComment' => [
|
||||
'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);
|
||||
}
|
||||
],
|
||||
'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) ) );
|
||||
}
|
||||
],
|
||||
'replies' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Number of replies to the content item",
|
||||
'resolve' => function ($result) {
|
||||
return self::replies($result);
|
||||
}
|
||||
],
|
||||
'relativeTimeKey' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns a string indicating the relative posting time of this item",
|
||||
'resolve' => function ($result) {
|
||||
return $result->streamSeparator();
|
||||
}
|
||||
],
|
||||
'author' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => "Author of this result",
|
||||
'resolve' => function ($result) {
|
||||
return \IPS\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) );
|
||||
}
|
||||
],
|
||||
'club' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::club(),
|
||||
'description' => "Club this result belongs to, if applicable",
|
||||
'resolve' => function ($result) {
|
||||
return self::club( $result );
|
||||
}
|
||||
],
|
||||
'reactions' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\Api\GraphQL\TypeRegistry::contentReaction() ),
|
||||
'description' => "Reactions for this content",
|
||||
'resolve' => function ($result) {
|
||||
try {
|
||||
$reactions = array();
|
||||
|
||||
if( \count( $result->reactions ) )
|
||||
{
|
||||
foreach( $result->reactions as $reactID => $count )
|
||||
{
|
||||
$reactions[] = array(
|
||||
'id' => $reactID,
|
||||
'reaction' => \IPS\Content\Reaction::load( $reactID ),
|
||||
'count' => $count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $reactions;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{}
|
||||
|
||||
return array();
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
protected static function getFieldValue( $field, $result )
|
||||
{
|
||||
$asArray = $result->asArray();
|
||||
return $asArray['indexData'][ $field ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of replies
|
||||
*
|
||||
* @param \IPS\Content\Search\Result Search result
|
||||
* @return string
|
||||
*/
|
||||
protected static function replies($result) {
|
||||
$asArray = $result->asArray();
|
||||
$itemData = $asArray['itemData'];
|
||||
|
||||
if ( \in_array( 'IPS\Content\Comment', class_parents( $asArray['indexData']['index_class'] ) ) )
|
||||
{
|
||||
$itemClass = static::getItemClass( $asArray );
|
||||
|
||||
if( isset( $itemClass::$databaseColumnMap['num_comments'] ) and isset( $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['num_comments'] ] ) )
|
||||
{
|
||||
if( $itemClass::$firstCommentRequired )
|
||||
{
|
||||
return $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['num_comments'] ] - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['num_comments'] ];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$indexClass = self::getFieldValue('index_class', $result);
|
||||
|
||||
if ( isset( $indexClass::$databaseColumnMap['num_comments'] ) and isset( $itemData[ $indexClass::$databasePrefix . $indexClass::$databaseColumnMap['num_comments'] ] ) )
|
||||
{
|
||||
if ( $indexClass::$firstCommentRequired )
|
||||
{
|
||||
return $itemData[ $indexClass::$databasePrefix . $indexClass::$databaseColumnMap['num_comments'] ] - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $itemData[ $indexClass::$databasePrefix . $indexClass::$databaseColumnMap['num_comments'] ];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content item title
|
||||
*
|
||||
* @param \IPS\Content\Search\Result Search result
|
||||
* @return string
|
||||
*/
|
||||
protected static function title( $result )
|
||||
{
|
||||
$asArray = $result->asArray();
|
||||
$itemClass = static::getItemClass( $asArray );
|
||||
|
||||
// If the content is used as the title (e.g. status updates), don't return anything
|
||||
if( $itemClass::$databaseColumnMap['title'] == 'content' )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $asArray['itemData'][ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['title'] ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the item URL
|
||||
*
|
||||
* @param \IPS\Content\Search\Result Search result
|
||||
* @return string
|
||||
*/
|
||||
protected static function url( $result )
|
||||
{
|
||||
$asArray = $result->asArray();
|
||||
$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\Review', class_parents( $asArray['indexData']['index_class'] ) ) )
|
||||
{
|
||||
$itemUrl = $itemUrl->setQueryString( array( 'do' => 'findReview', 'review' => $asArray['indexData']['index_object_id'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$itemUrl = $itemUrl->setQueryString( array( 'do' => 'findComment', 'comment' => $asArray['indexData']['index_object_id'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $itemUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unread status of this content
|
||||
*
|
||||
* @param \IPS\Content\Search\Result Search result
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function isUnread( $result )
|
||||
{
|
||||
$asArray = $result->asArray();
|
||||
$itemClass = static::getItemClass( $asArray );
|
||||
|
||||
if ( \in_array( 'IPS\Content\Comment', class_parents( $itemClass ) ) )
|
||||
{
|
||||
$contentClass = $itemClass::$contentItemClass;
|
||||
$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
|
||||
{
|
||||
$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 );
|
||||
}
|
||||
|
||||
return $unread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve contentImage
|
||||
*
|
||||
* @param array Array representation of \IPS\Content\Search\Result
|
||||
* @param array Arguments passed from resolver
|
||||
* @return string
|
||||
*/
|
||||
protected static function contentImages( $result )
|
||||
{
|
||||
try {
|
||||
$itemClass = static::getItemClass( $result->asArray() );
|
||||
$item = $itemClass::load( self::getFieldValue('index_item_id', $result) );
|
||||
$toReturn = array();
|
||||
|
||||
if ( $images = $item->contentImages( 20 ) )
|
||||
{
|
||||
foreach( $images as $image )
|
||||
{
|
||||
foreach( $image as $extension => $file )
|
||||
{
|
||||
$toReturn[] = (string) \IPS\File::get( $extension, $file )->url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $toReturn;
|
||||
} catch (\Exception $e) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve containerTitle
|
||||
*
|
||||
* @param array Array representation of \IPS\Content\Search\Result
|
||||
* @param array Arguments passed from resolver
|
||||
* @return string
|
||||
*/
|
||||
protected static function containerTitle( $result )
|
||||
{
|
||||
$result = $result->asArray();
|
||||
$itemClass = static::getItemClass( $result );
|
||||
$containerTitle = NULL;
|
||||
|
||||
if ( isset( $itemClass::$containerNodeClass ) )
|
||||
{
|
||||
$containerClass = $itemClass::$containerNodeClass;
|
||||
$containerTitle = $containerClass::titleFromIndexData( $result['indexData'], $result['itemData'], $result['containerData'], FALSE );
|
||||
}
|
||||
|
||||
return $containerTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve club
|
||||
*
|
||||
* @param array Array representation of \IPS\Content\Search\Result
|
||||
* @param array Arguments passed from resolver
|
||||
* @return \IPS\Member\Club
|
||||
*/
|
||||
protected static function club( $result )
|
||||
{
|
||||
if( self::getFieldValue('index_club_id', $result) )
|
||||
{
|
||||
return \IPS\Member\Club::load( self::getFieldValue('index_club_id', $result) );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item class for the result
|
||||
*
|
||||
* @param array Array representation of \IPS\Content\Search\Result
|
||||
* @return string
|
||||
*/
|
||||
protected static function getItemClass( $result )
|
||||
{
|
||||
$indexClass = $result['indexData']['index_class'];
|
||||
$itemClass = ( \in_array( 'IPS\Content\Comment', class_parents( $indexClass ) ) ) ? $indexClass::$itemClass : $indexClass;
|
||||
|
||||
return $itemClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Group Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* GroupType for GraphQL API
|
||||
*/
|
||||
class _GroupType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Group',
|
||||
'description' => 'Member groups',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Group ID",
|
||||
'resolve' => function ($group, $args, $context, $info) {
|
||||
return $group->g_id;
|
||||
}
|
||||
],
|
||||
'groupType' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'groupType',
|
||||
'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 )
|
||||
{
|
||||
return 'GUEST';
|
||||
}
|
||||
elseif( isset( \IPS\Member::administrators()['g'][ $group->g_id ] ) )
|
||||
{
|
||||
return 'ADMIN';
|
||||
}
|
||||
else
|
||||
{
|
||||
return 'MEMBER';
|
||||
}
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Group name",
|
||||
'args' => [
|
||||
'formatted' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => FALSE
|
||||
]
|
||||
],
|
||||
'resolve' => function ($group, $args, $context, $info) {
|
||||
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) {
|
||||
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) {
|
||||
return $group->g_access_offline;
|
||||
}
|
||||
],
|
||||
'canTag' => [
|
||||
'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'] );
|
||||
}
|
||||
],
|
||||
'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'];
|
||||
}
|
||||
],
|
||||
'members' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::member() ),
|
||||
'description' => "List of members in this group",
|
||||
'args' => [
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
]
|
||||
],
|
||||
'resolve' => function ($group, $args, $context, $info) {
|
||||
/* If we don't allow filtering by this group, don't return the members in it */
|
||||
if( $group->g_bitoptions['gbw_hide_group'] )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$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' );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Ignore option Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 29 Oct 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* IgnoreOptionType for GraphQL API
|
||||
*/
|
||||
class _IgnoreOptionType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_IgnoreOption',
|
||||
'description' => 'IgnoreOption type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Ignore option ID",
|
||||
'resolve' => function ($ignore) {
|
||||
return md5( $ignore['member_id'] . $ignore['type'] );
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Ignore type name",
|
||||
'resolve' => function ($ignore) {
|
||||
return $ignore['type'];
|
||||
}
|
||||
],
|
||||
'isBeingIgnored' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this member being ignored for the current content type?",
|
||||
'resolve' => function ($ignore) {
|
||||
return $ignore['is_being_ignored'];
|
||||
}
|
||||
],
|
||||
'lang' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The language string for this ignore type",
|
||||
'resolve' => function ($ignore) {
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'ignore_' . $ignore['type'] );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Language pack Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* LanguageType for GraphQL API
|
||||
*/
|
||||
class _LanguageType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Language',
|
||||
'description' => 'Language package',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Language name",
|
||||
'resolve' => function ($lang) {
|
||||
return $lang->title;
|
||||
}
|
||||
],
|
||||
'isDefault' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this language pack the site default?",
|
||||
'resolve' => function ($lang) {
|
||||
return (boolean) $lang->default;
|
||||
}
|
||||
],
|
||||
'locale' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the locale key for this language",
|
||||
'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 '';
|
||||
}
|
||||
],
|
||||
'phrases' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Accepts a JSON object, which should be an array of phrase keys to return. Returned values are key/value pairs representing phrase key and translation.",
|
||||
'args' => [
|
||||
'keys' => [
|
||||
'type' => TypeRegistry::nonNull( TypeRegistry::listOf( TypeRegistry::string() ) )
|
||||
]
|
||||
],
|
||||
'resolve' => function ($lang, $args) {
|
||||
$phrases = $args['keys'];
|
||||
$toReturn = array();
|
||||
|
||||
foreach( $phrases as $phrase )
|
||||
{
|
||||
$toReturn[ $phrase ] = $lang->addToStack( 'app_' . $phrase, FALSE, array('json' => TRUE) );
|
||||
}
|
||||
|
||||
return json_encode($toReturn);
|
||||
}
|
||||
],
|
||||
'phrase' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Get a phrase from this language pack",
|
||||
'args' => [
|
||||
'key' => [
|
||||
'type' => TypeRegistry::nonNull( TypeRegistry::string() )
|
||||
]
|
||||
],
|
||||
'resolve' => function ($lang, $args, $context, $info) {
|
||||
try
|
||||
{
|
||||
return $lang->addToStack( $args['key'] );
|
||||
}
|
||||
catch (\Exception $err)
|
||||
{
|
||||
return $args['key'];
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Login Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 29 Oct 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoginType for GraphQL API
|
||||
*/
|
||||
class _LoginType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Login',
|
||||
'description' => 'Login type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Handler ID",
|
||||
'resolve' => function ($login) {
|
||||
return $login->_id;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Login name",
|
||||
'resolve' => function ($login) {
|
||||
return \IPS\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() );
|
||||
}
|
||||
],
|
||||
'icon' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "URL to the icon for this service",
|
||||
'resolve' => function ($login) {
|
||||
return $login->buttonIcon();
|
||||
}
|
||||
],
|
||||
'color' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Color of this handler's button",
|
||||
'resolve' => function ($login) {
|
||||
return $login->buttonColor();
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'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);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,689 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Member Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MemberrType for GraphQL API
|
||||
*/
|
||||
class _MemberType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Member',
|
||||
'description' => 'Community members',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Returns the member's ID",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return $member->member_id;
|
||||
}
|
||||
],
|
||||
'email' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the member's email address (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return $member->email;
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the URL to member's profile",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return (string) $member->url();
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the member's username",
|
||||
'args' => [
|
||||
'formatted' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => FALSE
|
||||
]
|
||||
],
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return ( $args['formatted'] ) ? \IPS\Member\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 ) {
|
||||
return $member->member_title;
|
||||
}
|
||||
],
|
||||
'timezone' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the member's timezone (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return $member->timezone;
|
||||
}
|
||||
],
|
||||
'joined' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the date the member joined",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return $member->joined->getTimestamp();
|
||||
}
|
||||
],
|
||||
'notifications' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::notification() ),
|
||||
'description' => "Returns the member's notifications (depending on permissions)",
|
||||
'args' => [
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0,
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
'sortBy' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'notification_sort',
|
||||
'values' => ['updated_time', 'sent_time', 'read_time', 'unread']
|
||||
]),
|
||||
'defaultValue' => 'updated_time'
|
||||
],
|
||||
'sortDir' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'notification_sort_dir',
|
||||
'values' => ['asc', 'desc']
|
||||
]),
|
||||
'defaultValue' => 'desc'
|
||||
],
|
||||
'unread' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'defaultValue' => NULL
|
||||
]
|
||||
],
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isOwnerMember($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return self::notifications($member, $args, $context);
|
||||
}
|
||||
],
|
||||
'notificationCount' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the member's notification count (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isOwnerMember($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return $member->notification_cnt;
|
||||
}
|
||||
],
|
||||
'posts' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the member's post count",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return $member->member_posts;
|
||||
}
|
||||
],
|
||||
'contentCount' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the member's post count",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
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;
|
||||
}
|
||||
],
|
||||
'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() )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*) as count', 'core_solved_index', array( 'member_id=?', $member->member_id ) )->first();
|
||||
return $count;
|
||||
}
|
||||
],
|
||||
'ip_address' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the member's IP address (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return $member->ip_address;
|
||||
}
|
||||
],
|
||||
'warn_level' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the member's warning level (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) ){
|
||||
return NULL;
|
||||
}
|
||||
return $member->warn_level;
|
||||
}
|
||||
],
|
||||
'profileViews' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns the number of profile views for this member (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return $member->members_profile_views;
|
||||
}
|
||||
],
|
||||
'validating' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Returns the member's validating status (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return (bool) $member->members_bitoptions['validating'];
|
||||
}
|
||||
],
|
||||
'group' => [
|
||||
'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 );
|
||||
}
|
||||
],
|
||||
'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() );
|
||||
}
|
||||
],
|
||||
'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();
|
||||
}
|
||||
],
|
||||
'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();
|
||||
}
|
||||
],
|
||||
'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();
|
||||
}
|
||||
],
|
||||
'secondaryGroups' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::group() ),
|
||||
'description' => "Returns the member's secondary groups (depending on permissions)",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
if( !self::isAuthorized($member) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return self::secondaryGroups( $member, $args, $context, $info );
|
||||
}
|
||||
],
|
||||
'defaultStream' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "The ID of the user's default stream",
|
||||
'resolve' => function ($member) {
|
||||
if( !self::isAuthorized($member) || $member->defaultStream == FALSE )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $member->defaultStream;
|
||||
}
|
||||
],
|
||||
'allowFollow' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether this member allows others to follow them",
|
||||
'resolve' => function ($member) {
|
||||
return !$member->members_bitoptions['pp_setting_moderate_followers'];
|
||||
}
|
||||
],
|
||||
'follow' => [
|
||||
'type' => TypeRegistry::follow(),
|
||||
'description' => "Returns fields to handle followers/following",
|
||||
'resolve' => function ($member) {
|
||||
return array(
|
||||
'app' => 'core',
|
||||
'area' => 'member',
|
||||
'id' => $member->member_id,
|
||||
'member' => $member
|
||||
);
|
||||
}
|
||||
],
|
||||
'content' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::searchResult() ),
|
||||
'args' => [
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
],
|
||||
'description' => "Returns the member's content",
|
||||
'resolve' => function ($member, $args) {
|
||||
return self::content($member, $args);
|
||||
}
|
||||
],
|
||||
'clubs' => [
|
||||
'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' );
|
||||
}
|
||||
],
|
||||
'photo' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the member's photo",
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return self::photo($member);
|
||||
}
|
||||
],
|
||||
'coverPhoto' => \IPS\Api\GraphQL\Fields\CoverPhotoField::getDefinition('core_MemberCoverPhoto'),
|
||||
'customFieldGroups' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::profileFieldGroup() ),
|
||||
'resolve' => function( $member, $args, $context, $info ) {
|
||||
return self::customFieldGroups( $member, $args, $context );
|
||||
}
|
||||
],
|
||||
'maxUploadSize' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "The maximum upload size allowed by this user, either globally or per-item (whichever is smaller). NULL indicates no limit.",
|
||||
'resolve' => function( $member ) {
|
||||
if( $member->member_id && !self::isOwnerMember( $member ) )
|
||||
{
|
||||
throw new \IPS\Api\GraphQL\SafeException( 'INVALID_USER', '2S401/1', 403 );
|
||||
}
|
||||
|
||||
return \IPS\Helpers\Form\Editor::maxTotalAttachmentSize( $member, 0 );
|
||||
}
|
||||
],
|
||||
'canBeIgnored' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Can this member be ignored by the current user?",
|
||||
'resolve' => function ($member) {
|
||||
return $member->canBeIgnored();
|
||||
}
|
||||
],
|
||||
'ignoreStatus' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::ignoreOption() ),
|
||||
'description' => "Returns the ignore status of this member, based on the currently-authenticated member's preferences.",
|
||||
'resolve' => function ($member ) {
|
||||
return self::ignoreStatus($member);
|
||||
}
|
||||
],
|
||||
|
||||
// Messenger fields
|
||||
'messengerDisabled' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this member's messenger disabled?",
|
||||
'resolve' => function( $member ) {
|
||||
return (bool) $member->members_disable_pm;
|
||||
}
|
||||
],
|
||||
'messengerNewCount' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Number of new messages",
|
||||
'resolve' => function ($member) {
|
||||
if( $member->member_id && !self::isOwnerMember( $member ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $member->msg_count_new;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a member's photo
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return string
|
||||
*/
|
||||
protected static function photo($member)
|
||||
{
|
||||
$member_properties = array();
|
||||
|
||||
foreach( array( 'name', 'pp_main_photo', 'pp_photo_type', 'pp_thumb_photo', 'member_id' ) as $column )
|
||||
{
|
||||
$member_properties[ $column ] = $member->$column;
|
||||
}
|
||||
|
||||
$photoUrl = \IPS\Member::photoUrl( $member_properties );
|
||||
|
||||
if( mb_strpos( $photoUrl, "data:image/svg+xml" ) === 0 )
|
||||
{
|
||||
return static::getLetterPhotoData( $member );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $photoUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a json array containing letter/color combo for a user's letter photo
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return string
|
||||
*/
|
||||
protected static function getLetterPhotoData($member)
|
||||
{
|
||||
return json_encode( \IPS\Member::generateLetterPhoto( array(
|
||||
'name' => $member->name,
|
||||
'pp_main_photo' => $member->pp_main_photo,
|
||||
'member_id' => $member->member_id
|
||||
), TRUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a user authorized to access sensitive data
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function isAuthorized($member)
|
||||
{
|
||||
return self::isOwnerMember($member) || \IPS\Member::loggedIn()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this user is the same as the user requesting the info
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function isOwnerMember($member)
|
||||
{
|
||||
return $member->member_id && $member->member_id == \IPS\Member::loggedIn()->member_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a member's content
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function content($member, $args)
|
||||
{
|
||||
// Get page
|
||||
// We don't know the count at this stage, so figure out the page number from
|
||||
// our offset/limit
|
||||
$page = 1;
|
||||
$offset = max( $args['offset'], 0 );
|
||||
$limit = min( $args['limit'], 50 );
|
||||
|
||||
if( $offset > 0 )
|
||||
{
|
||||
$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 );
|
||||
|
||||
return $latestActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user's notifications
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @param array $args
|
||||
* @param array $context
|
||||
* @return array
|
||||
*/
|
||||
protected static function notifications($member, $args, $context)
|
||||
{
|
||||
/* 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 );
|
||||
|
||||
/* Are we filtering by unread status? */
|
||||
if( isset( $args['unread'] ) && $args['unread'] !== NULL )
|
||||
{
|
||||
if( $args['unread'] === TRUE )
|
||||
{
|
||||
$where[] = array( "read_time IS NULL" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( "read_time IS NOT NULL" );
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorting */
|
||||
$sort = $args['sortBy'] . ' ' . $args['sortDir'];
|
||||
|
||||
if( $args['sortBy'] == 'unread' )
|
||||
{
|
||||
$sort = $args['sortDir'] == 'desc' ? 'read_time IS NOT NULL, sent_time DESC' : 'read_time IS NULL, sent_time ASC';
|
||||
}
|
||||
|
||||
/* Get Count */
|
||||
$count = \IPS\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 )
|
||||
{
|
||||
try
|
||||
{
|
||||
$notification = \IPS\Notification\Api::constructFromData( $row );
|
||||
$returnRows[] = array( 'notification' => $notification, 'data' => $notification->getData( FALSE ) );
|
||||
}
|
||||
catch ( \LogicException $e ) { }
|
||||
}
|
||||
|
||||
return $returnRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return custom profile field groups
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function customFieldGroups($member, $args, $context)
|
||||
{
|
||||
/* Get profile field values */
|
||||
try
|
||||
{
|
||||
$profileFieldValues = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( !empty( $profileFieldValues ) )
|
||||
{
|
||||
$fields = array();
|
||||
|
||||
if( \IPS\Member::loggedIn()->isAdmin() OR \IPS\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 )
|
||||
{
|
||||
$where = array( "pfd.pf_member_hide='owner' OR pfd.pf_member_hide='all'" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$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(
|
||||
array('core_pfields_groups', 'pfg'),
|
||||
"pfd.pf_group_id=pfg.pf_group_id"
|
||||
), 'IPS\core\ProfileFields\Field' ) as $field )
|
||||
{
|
||||
if( $profileFieldValues[ 'field_' . $field->id ] !== '' AND $profileFieldValues[ 'field_' . $field->id ] !== NULL )
|
||||
{
|
||||
if( !isset( $fields[ $field->group_id ] ) ){
|
||||
$fields[ $field->group_id ] = array(
|
||||
'id' => md5( $member->member_id . $field->group_id ),
|
||||
'groupId' => $field->group_id,
|
||||
'title' => 'core_pfieldgroups_' . $field->group_id,
|
||||
'fields' => array()
|
||||
);
|
||||
}
|
||||
|
||||
$fields[ $field->group_id ]['fields'][] = array(
|
||||
'id' => md5( $member->member_id . $field->id ),
|
||||
'fieldId' => $field->id,
|
||||
'title' => 'core_pfield_' . $field->id,
|
||||
'value' => json_encode( $field->apiValue( $profileFieldValues['field_' . $field->id] ) ),
|
||||
'type' => $field->type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve followers field
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @param array Arguments passed to this resolver
|
||||
* @return array
|
||||
*/
|
||||
protected static function followers($member, $args, $context, $info)
|
||||
{
|
||||
$limit = min( $args['limit'], 50 );
|
||||
|
||||
return array_map(
|
||||
function ($followRow)
|
||||
{
|
||||
return \IPS\Member::load( $followRow['follow_member_id'] );
|
||||
},
|
||||
iterator_to_array( $member->followers( 3, array( 'immediate', 'daily', 'weekly' ), NULL, array(0, $limit ) ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve secondary groups field
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @param array Arguments passed to this resolver
|
||||
* @return array
|
||||
*/
|
||||
protected static function secondaryGroups($member, $args, $context, $info)
|
||||
{
|
||||
$secondaryGroups = array();
|
||||
foreach ( array_filter( array_map( "intval", explode( ',', $member->mgroup_others ) ) ) as $secondaryGroupId )
|
||||
{
|
||||
try
|
||||
{
|
||||
$secondaryGroups[] = \IPS\Member\Group::load( $secondaryGroupId );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
return $secondaryGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve ignore status field
|
||||
*
|
||||
* @param \IPS\Member
|
||||
* @return array
|
||||
*/
|
||||
protected static function ignoreStatus($member)
|
||||
{
|
||||
if( !$member->canBeIgnored() || $member->member_id === \IPS\Member::loggedIn()->member_id ){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$ignore = FALSE;
|
||||
|
||||
try
|
||||
{
|
||||
$ignore = \IPS\core\Ignore::load( $member->member_id, 'ignore_ignore_id', array( 'ignore_owner_id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {
|
||||
// Just keep ignore as false
|
||||
}
|
||||
|
||||
$ignoreStatus = array();
|
||||
|
||||
foreach ( \IPS\core\Ignore::types() as $type )
|
||||
{
|
||||
$ignoreStatus[] = array(
|
||||
'type' => $type,
|
||||
'is_being_ignored' => !$ignore ? $ignore : $ignore->$type
|
||||
);
|
||||
}
|
||||
|
||||
return $ignoreStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger conversation Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MessengerConversation for GraphQL API
|
||||
*/
|
||||
class _MessengerConversationType extends \IPS\Content\Api\GraphQL\ItemType
|
||||
{
|
||||
/*
|
||||
* @brief The item classname we use for this type
|
||||
*/
|
||||
protected static $itemClass = '\IPS\core\Messenger\Conversation';
|
||||
|
||||
/*
|
||||
* @brief GraphQL type name
|
||||
*/
|
||||
protected static $typeName = 'core_MessengerConversation';
|
||||
|
||||
/*
|
||||
* @brief GraphQL type description
|
||||
*/
|
||||
protected static $typeDescription = 'A messenger conversation';
|
||||
|
||||
/**
|
||||
* Return the fields available in this type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields()
|
||||
{
|
||||
$defaultFields = parent::fields();
|
||||
$conversationFields = array(
|
||||
'folder' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::messengerFolder(),
|
||||
'description' => "Returns the folder ID that this conversation exists in for the logged-in user",
|
||||
'resolve' => function ($message) {
|
||||
$folderObj = \IPS\core\api\GraphQL\TypeRegistry::messengerFolder();
|
||||
$folders = $folderObj->getMemberFolders();
|
||||
|
||||
if( isset( $folders[ $message->map['map_folder_id'] ] ) )
|
||||
{
|
||||
return $folders[ $message->map['map_folder_id'] ];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'participants' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerParticipant() ),
|
||||
'description' => "Returns a list of participants in this conversation",
|
||||
'resolve' => function ($message) {
|
||||
return $message->maps();
|
||||
}
|
||||
],
|
||||
'activeParticipants' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Returns a count of active participants in this conversation",
|
||||
'resolve' => function ($message) {
|
||||
return $message->activeParticipants;
|
||||
}
|
||||
],
|
||||
'participantBlurb' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "A short blurb containing the names of the participants",
|
||||
'resolve' => function ($message) {
|
||||
return $message->participantBlurb();
|
||||
}
|
||||
],
|
||||
'lastMessage' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::messengerReply(),
|
||||
'description' => "The most recent reply to the conversation",
|
||||
'resolve' => function ($message) {
|
||||
return $message->comments( 1, 0, 'date', 'desc' );
|
||||
}
|
||||
],
|
||||
'updated' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Timestamp of when this conversation was last updated",
|
||||
'resolve' => function ($message) {
|
||||
return $message->last_post_time;
|
||||
}
|
||||
],
|
||||
'isUnread' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this conversation unread?",
|
||||
'resolve' => function ($message) {
|
||||
return (bool) $message->map['map_has_unread'];
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// Remove duplicated fields
|
||||
unset( $defaultFields['container'] );
|
||||
unset( $defaultFields['views'] );
|
||||
unset( $defaultFields['isLocked'] );
|
||||
unset( $defaultFields['isPinned'] );
|
||||
unset( $defaultFields['isFeatured'] );
|
||||
unset( $defaultFields['hiddenStatus'] );
|
||||
unset( $defaultFields['updated'] );
|
||||
unset( $defaultFields['follow'] );
|
||||
unset( $defaultFields['tags'] );
|
||||
unset( $defaultFields['hasPoll'] );
|
||||
unset( $defaultFields['poll'] );
|
||||
|
||||
|
||||
return array_merge( $defaultFields, $conversationFields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return item permission fields.
|
||||
* Here we adjust the resolver for the commentInformation field to check whether this is
|
||||
* a poll-only topic.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getItemPermissionFields()
|
||||
{
|
||||
$defaultFields = parent::getItemPermissionFields();
|
||||
|
||||
// Messenger does not restrict based on locked status etc.
|
||||
$defaultFields['commentInformation']['resolve'] = function () {
|
||||
return NULL;
|
||||
};
|
||||
|
||||
return $defaultFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the available sorting options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOrderByOptions()
|
||||
{
|
||||
// Note: these options all get prefixed with 'mt_' when passed into the query.
|
||||
return array('last_post_time', 'start_time', 'replies');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comment type that goes with this item type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
protected static function getCommentType()
|
||||
{
|
||||
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);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger folder Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 27 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MessengerFolder for GraphQL API
|
||||
*/
|
||||
class _MessengerFolderType extends ObjectType
|
||||
{
|
||||
|
||||
public $memberFolders = NULL;
|
||||
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_MessengerFolder',
|
||||
'description' => 'Messenger folder',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Folder ID",
|
||||
'resolve' => function ($folder) {
|
||||
return $folder['id'];
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The name of this folder (as provided by the user)",
|
||||
'resolve' => function ($folder) {
|
||||
return $folder['name'];
|
||||
}
|
||||
],
|
||||
'count' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Number of conversations in this folder",
|
||||
'resolve' => function ($folder) {
|
||||
return $folder['count'];
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'description' => "URL to this folder",
|
||||
'resolve' => function ($folder) {
|
||||
return $folder['url'];
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of messenger folder data for the logged-in member
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if( $this->memberFolders !== NULL && !$refetch )
|
||||
{
|
||||
return $this->memberFolders;
|
||||
}
|
||||
|
||||
/* Get folders */
|
||||
$folders = array( 'myconvo' => \IPS\Member::loggedIn()->language()->addToStack('messenger_folder_inbox') );
|
||||
if ( \IPS\Member::loggedIn()->pconversation_filters )
|
||||
{
|
||||
$folders = $folders + array_filter( json_decode( \IPS\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' ) );
|
||||
$folderCounts = array();
|
||||
foreach( $counts AS $k => $count )
|
||||
{
|
||||
$folderCounts[$count['map_folder_id']] = $count['count'];
|
||||
}
|
||||
|
||||
foreach( $folders as $id => $name)
|
||||
{
|
||||
$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
|
||||
);
|
||||
}
|
||||
|
||||
return $this->memberFolders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger participant Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 27 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MessengerParticipant for GraphQL API
|
||||
*/
|
||||
class _MessengerParticipantType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_MessengerParticipant',
|
||||
'description' => 'Messenger participant',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Participant ID (this is not the member ID, but an ID relative to the conversation)",
|
||||
'resolve' => function ($map) {
|
||||
return $map['map_id'];
|
||||
}
|
||||
],
|
||||
'member' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => "The member",
|
||||
'resolve' => function ($map) {
|
||||
if( $map['map_user_id'] )
|
||||
{
|
||||
return \IPS\Member::load( $map['map_user_id'] );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'isActive' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => 'Is the member active in this conversation?',
|
||||
'resolve' => function ($map) {
|
||||
return $map['map_user_active'];
|
||||
}
|
||||
],
|
||||
'isBanned' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => 'Has the member been removed from this conversation?',
|
||||
'resolve' => function ($map) {
|
||||
return $map['map_user_banned'];
|
||||
}
|
||||
],
|
||||
'lastRead' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => 'Timestamp of when the member last read this conversation',
|
||||
'resolve' => function ($map) {
|
||||
if( $map['map_read_time'] !== 0 )
|
||||
{
|
||||
return $map['map_read_time'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'leftDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => 'The timestamp of when the participant left the conversation if they are no longer active',
|
||||
'resolve' => function ($map) {
|
||||
if( !$map['map_user_active'] && $map['map_left_time'] )
|
||||
{
|
||||
return $map['map_left_time'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'lastReply' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => 'The timestamp of when the participant last replied',
|
||||
'resolve' => function ($map) {
|
||||
if( $map['map_last_topic_reply'] )
|
||||
{
|
||||
return $map['map_last_topic_reply'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Messenger reply Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Sep 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MessengerReply for GraphQL API
|
||||
*/
|
||||
class _MessengerReplyType extends \IPS\Content\Api\GraphQL\CommentType
|
||||
{
|
||||
/*
|
||||
* @brief The item classname we use for this type
|
||||
*/
|
||||
protected static $commentClass = '\IPS\core\Messenger\Message';
|
||||
|
||||
/*
|
||||
* @brief GraphQL type name
|
||||
*/
|
||||
protected static $typeName = 'core_MessengerReply';
|
||||
|
||||
/*
|
||||
* @brief GraphQL type description
|
||||
*/
|
||||
protected static $typeDescription = 'A reply';
|
||||
|
||||
/**
|
||||
* Get the item type that goes with this item type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public static function getItemType()
|
||||
{
|
||||
return \IPS\core\api\GraphQL\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);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Mobile Menu Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 12 June 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile Menu Type for GraphQL API
|
||||
*/
|
||||
class _MobileMenuType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_MobileMenu',
|
||||
'description' => 'Mobile menu item type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Item ID",
|
||||
'resolve' => function ( $item ) {
|
||||
return $item->id;
|
||||
}
|
||||
],
|
||||
'class' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Class which handles this item",
|
||||
'resolve' => function ( $item ) {
|
||||
return \get_class( $item );
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Menu item title",
|
||||
'resolve' => function ($item) {
|
||||
return $item->title();
|
||||
}
|
||||
],
|
||||
'icon' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Icon key for this item",
|
||||
'resolve' => function ($item) {
|
||||
return $item->icon();
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'description' => "URL to content",
|
||||
'resolve' => function ( $item ) {
|
||||
return $item->link();
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Notification method type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Jan 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationMethodType for GraphQL API
|
||||
*/
|
||||
class _NotificationMethodType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_NotificationMethod',
|
||||
'description' => 'Notification method',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Method name",
|
||||
'resolve' => function ($method) {
|
||||
return $method['name'];
|
||||
}
|
||||
],
|
||||
'disabled' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this method disabled?",
|
||||
'resolve' => function ($method) {
|
||||
return $method['disabled'];
|
||||
}
|
||||
],
|
||||
'default' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "THe default state for this method",
|
||||
'resolve' => function ($method) {
|
||||
return $method['default'];
|
||||
}
|
||||
],
|
||||
'value' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "The member's setting",
|
||||
'resolve' => function ($method) {
|
||||
return $method['member'];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: NotificationType group Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationType for GraphQL API
|
||||
*/
|
||||
class _NotificationType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Notification',
|
||||
'description' => 'Notification',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Notification ID",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->id;
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Notification type",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->notification_key;
|
||||
}
|
||||
],
|
||||
'app' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Notification app",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->app;
|
||||
}
|
||||
],
|
||||
'class' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Notification content class",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->item_class;
|
||||
}
|
||||
],
|
||||
'itemID' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "ID of the content item",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->item_id;
|
||||
}
|
||||
],
|
||||
'sentDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Sent timestamp",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->sent;
|
||||
}
|
||||
],
|
||||
'updatedDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Updated timestamp",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->updated;
|
||||
}
|
||||
],
|
||||
'readDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Read timestamp",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['notification']->read_time;
|
||||
}
|
||||
],
|
||||
'author' => [
|
||||
'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;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Notification title",
|
||||
'resolve' => function ($notification) {
|
||||
return isset( $notification['data']['title'] ) ? $notification['data']['title'] : NULL;
|
||||
}
|
||||
],
|
||||
'content' => [
|
||||
'type' => TypeRegistry::richText(),
|
||||
'description' => "Notification content",
|
||||
'resolve' => function ($notification) {
|
||||
return isset( $notification['data']['content'] ) ? $notification['data']['content'] : NULL;
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'description' => "URL to content that triggered notification",
|
||||
'resolve' => function ($notification) {
|
||||
return $notification['data']['url'];
|
||||
}
|
||||
],
|
||||
'unread' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this notification unread by the recipient?",
|
||||
'resolve' => function ($notification) {
|
||||
return (bool) $notification['data']['unread'];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Notification types
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Jan 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationTypeType for GraphQL API
|
||||
*/
|
||||
class _NotificationTypeType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_NotificationType',
|
||||
'description' => 'Notification type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Type ID",
|
||||
'resolve' => function ($type) {
|
||||
return $type['id'];
|
||||
}
|
||||
],
|
||||
'extension' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Type extension",
|
||||
'resolve' => function ($type) {
|
||||
return $type['extension'];
|
||||
}
|
||||
],
|
||||
'group' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Title of the notification group",
|
||||
'resolve' => function ($type) {
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'notifications__' . $type['extension'] );
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Type type",
|
||||
'resolve' => function ($type) {
|
||||
return $type['type'];
|
||||
}
|
||||
],
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Type name",
|
||||
'resolve' => function ($type) {
|
||||
return $type['name'];
|
||||
}
|
||||
],
|
||||
'description' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Type description",
|
||||
'resolve' => function ($type) {
|
||||
return \IPS\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'] );
|
||||
}
|
||||
],
|
||||
'inline' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::notificationMethod(),
|
||||
'description' => "Inline notification method",
|
||||
'resolve' => function ($type) {
|
||||
return $type['inline'];
|
||||
}
|
||||
],
|
||||
'email' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::notificationMethod(),
|
||||
'description' => "Email notification method",
|
||||
'resolve' => function ($type) {
|
||||
return $type['email'];
|
||||
}
|
||||
],
|
||||
'push' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::notificationMethod(),
|
||||
'description' => "Push notification method",
|
||||
'resolve' => function ($type) {
|
||||
if ( !isset( $type['push'] ) || !\count( $type['push'] ) || !\IPS\Settings::i()->mobile_app_id || !\IPS\Member::loggedIn()->members_bitoptions['mobile_notifications'] )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $type['push'];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Poll Question Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Nov 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PollQuestionType for GraphQL API
|
||||
*/
|
||||
class _PollQuestionType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_PollQuestion',
|
||||
'description' => 'A poll question',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Returns the poll question ID",
|
||||
'resolve' => function ($question) {
|
||||
return $question['id'];
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Returns the question title",
|
||||
'resolve' => function ($question) {
|
||||
return $question['data']['question'];
|
||||
}
|
||||
],
|
||||
'isMultiChoice' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this question multiple-choice?",
|
||||
'resolve' => function ($question) {
|
||||
return $question['data']['multi'];
|
||||
}
|
||||
],
|
||||
'votes' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "How many users voted on this question",
|
||||
'resolve' => function ($question) {
|
||||
return isset( $question['data']['votes'] ) ? array_sum( $question['data']['votes'] ) : 0;
|
||||
}
|
||||
],
|
||||
'choices' => [
|
||||
'type' => TypeRegistry::listOf( new ObjectType([
|
||||
'name' => 'core_PollChoice',
|
||||
'description' => "A poll choice",
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'resolve' => function ($choice) {
|
||||
return $choice['id'];
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'resolve' => function ($choice) {
|
||||
return $choice['title'];
|
||||
}
|
||||
],
|
||||
'votes' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'resolve' => function ($choice) {
|
||||
return $choice['votes'];
|
||||
}
|
||||
],
|
||||
'votedFor' => [
|
||||
'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'] );
|
||||
}
|
||||
],
|
||||
'voters' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::member() ),
|
||||
'args' => [
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
]
|
||||
],
|
||||
'resolve' => function ($choice, $args) {
|
||||
$members = array();
|
||||
$offset = max( $args['offset'], 0 );
|
||||
$limit = min( $args['limit'], 50 );
|
||||
$membersToLoad = \array_slice( $choice['voters'], $offset, $limit );
|
||||
|
||||
foreach( $membersToLoad as $memberID )
|
||||
{
|
||||
$members[] = \IPS\Member::load( $memberID );
|
||||
}
|
||||
|
||||
return $members;
|
||||
}
|
||||
]
|
||||
]
|
||||
]) ),
|
||||
'description' => "The choices for this question",
|
||||
'resolve' => function ($question) {
|
||||
$choices = array();
|
||||
$canViewCount = \IPS\Member::loggedIn()->modPermission('can_edit_poll_votes');
|
||||
|
||||
foreach( $question['data']['choice'] as $idx => $choice )
|
||||
{
|
||||
$choices[ $idx ] = array(
|
||||
'id' => $question['id'] . '-c' . $idx,
|
||||
'poll' => $question['poll'],
|
||||
'title' => $choice,
|
||||
'votes' => isset( $question['data']['votes'] ) ? $question['data']['votes'][ $idx ] : 0,
|
||||
'voters' => isset( $question['voters'][ $idx ] ) ? $question['voters'][ $idx ] : array()
|
||||
);
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Poll Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 30 Nov 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PollType for GraphQL API
|
||||
*/
|
||||
class _PollType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Poll',
|
||||
'description' => 'Polls',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Returns the poll ID",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->pid;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The poll question",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->poll_question;
|
||||
}
|
||||
],
|
||||
'votes' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Number of votes on this poll",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->votes;
|
||||
}
|
||||
],
|
||||
'questions' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::pollQuestion() ),
|
||||
'description' => "The poll questions",
|
||||
'resolve' => function ($poll) {
|
||||
return static::getQuestions($poll);
|
||||
}
|
||||
],
|
||||
'closeTimestamp' => [
|
||||
'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 )
|
||||
{
|
||||
return $poll->poll_close_date->getTimestamp();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'isClosed' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether this poll is closed to further votes",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->poll_closed;
|
||||
}
|
||||
],
|
||||
'isPublic' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether this is a public poll (i.e. voter names are public)",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->poll_view_voters;
|
||||
}
|
||||
],
|
||||
'hasVoted' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Has the user voted in this poll?",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->getVote() !== NULL;
|
||||
}
|
||||
],
|
||||
'canVote' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether the user can vote in this poll",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->canVote();
|
||||
}
|
||||
],
|
||||
'canViewResults' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether the user can view the results of this poll",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->canViewResults();
|
||||
}
|
||||
],
|
||||
'canViewVoters' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether the user can view who voted, based on permissions",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->canSeeVoters();
|
||||
}
|
||||
],
|
||||
'canClose' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether the user can close this poll",
|
||||
'resolve' => function ($poll) {
|
||||
return $poll->canClose();
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve poll questions field
|
||||
*
|
||||
* @param \IPS\Poll
|
||||
* @return array
|
||||
*/
|
||||
protected static function getQuestions ($poll) {
|
||||
$questions = array();
|
||||
|
||||
foreach ( $poll->choices as $idx => $choice )
|
||||
{
|
||||
// We provide the index as a ID since questions don't have a real ID. Providing an ID
|
||||
// allows GraphQL (well, Apollo) to optimize queries.
|
||||
// We need to pass `poll` through here since there's no way to obtain a reference to the poll
|
||||
// from the individual question/choices
|
||||
$questions[ $idx ] = array(
|
||||
'id' => 'p' . $poll->pid . '-q' . $idx,
|
||||
'data' => $choice,
|
||||
'poll' => $poll,
|
||||
'voters' => array()
|
||||
);
|
||||
}
|
||||
|
||||
if( $poll->canSeeVoters() )
|
||||
{
|
||||
// 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 ) );
|
||||
|
||||
foreach( $query as $voter )
|
||||
{
|
||||
if( $voter['member_choices'] !== NULL )
|
||||
{
|
||||
$voteData = json_decode( $voter['member_choices'] );
|
||||
foreach( $voteData as $questionIdx => $answer )
|
||||
{
|
||||
if( \is_array( $answer ) )
|
||||
{
|
||||
foreach( $answer as $_answer )
|
||||
{
|
||||
$questions[ $questionIdx ]['voters'][ \intval( $_answer ) ][] = $voter['member_id'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$questions[ $questionIdx ]['voters'][ \intval( $answer ) ][] = $voter['member_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $questions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Popular Contributors Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 11 Feb 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PopularContributorsType for GraphQL API
|
||||
*/
|
||||
class _PopularContributorType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_PopularContributor',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'rep' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'resolve' => function ($contributor, $args) {
|
||||
return $contributor['rep'];
|
||||
}
|
||||
],
|
||||
'user' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'resolve' => function ($contributor, $args) {
|
||||
if( $contributor['member_id'] )
|
||||
{
|
||||
return \IPS\Member::load( $contributor['member_id'] );
|
||||
}
|
||||
|
||||
return new \IPS\Member;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Profile field group Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* StreamType for GraphQL API
|
||||
*/
|
||||
class _ProfileFieldGroupType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ProfileFieldGroup',
|
||||
'description' => 'Custom profile field groups',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Profile field group ID (relative to member)",
|
||||
'resolve' => function ($fieldGroup) {
|
||||
return $fieldGroup['id'];
|
||||
}
|
||||
],
|
||||
'groupId' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Actual profile field group ID",
|
||||
'resolve' => function ($fieldGroup) {
|
||||
return $fieldGroup['groupId'];
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Profile field group title",
|
||||
'resolve' => function ($fieldGroup) {
|
||||
return \IPS\Member::loggedIn()->language()->get( $fieldGroup['title'] );
|
||||
}
|
||||
],
|
||||
'fields' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::profileField() ),
|
||||
'description' => "List of fields in this profile field group",
|
||||
'resolve' => function ($fieldGroup) {
|
||||
return $fieldGroup['fields'];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Profile field Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ProfileFieldType for GraphQL API
|
||||
*/
|
||||
class _ProfileFieldType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$config = [
|
||||
'name' => 'core_ProfileField',
|
||||
'description' => 'Custom profile field',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Profile field ID, relative to member",
|
||||
'resolve' => function ($field) {
|
||||
return $field['id'];
|
||||
}
|
||||
],
|
||||
'fieldId' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Actual profile field ID",
|
||||
'resolve' => function ($field) {
|
||||
return $field['fieldId'];
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Profile field title",
|
||||
'resolve' => function ($field) {
|
||||
return \IPS\Member::loggedIn()->language()->get( $field['title'] );
|
||||
}
|
||||
],
|
||||
'value' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Profile field value",
|
||||
'resolve' => function ($field) {
|
||||
return $field['value'];
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Profile field type",
|
||||
'resolve' => function ($field) {
|
||||
return $field['type'];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: PromotedItemType group Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PromotedItemType for GraphQL API
|
||||
*/
|
||||
class _PromotedItemType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_PromotedItem',
|
||||
'description' => 'Promoted Item',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => 'Promote ID',
|
||||
'resolve' => function ($item) {
|
||||
return $item->id;
|
||||
}
|
||||
],
|
||||
'addedBy' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => 'Who promoted this item',
|
||||
'resolve' => function ($item) {
|
||||
return \IPS\Member::load( $item->added_by );
|
||||
}
|
||||
],
|
||||
'images' => [
|
||||
'type' => TypeRegistry::listOf( TypeRegistry::string() ),
|
||||
'description' => 'Photos attached to the promoted item',
|
||||
'resolve' => function ($item) {
|
||||
$images = array();
|
||||
|
||||
if( \count( $item->imageObjects() ) )
|
||||
{
|
||||
foreach( $item->imageObjects() as $file )
|
||||
{
|
||||
$images[] = (string) $file->url;
|
||||
}
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => 'Promoted title',
|
||||
'resolve' => function ($item) {
|
||||
return $item->ourPicksTitle;
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::url(),
|
||||
'description' => 'URL to original item',
|
||||
'resolve' => function ($item) {
|
||||
return $item->object()->url();
|
||||
}
|
||||
],
|
||||
'item' => [
|
||||
'type' => new UnionType([
|
||||
'name' => 'core_PromotedItem_TypeUnion',
|
||||
'types' => [
|
||||
\IPS\Content\Api\GraphQL\TypeRegistry::comment(),
|
||||
\IPS\Content\Api\GraphQL\TypeRegistry::item(),
|
||||
\IPS\Node\Api\GraphQL\TypeRegistry::node()
|
||||
],
|
||||
'resolveType' => function ($item) {
|
||||
if ( $item instanceof \IPS\Content\Comment )
|
||||
{
|
||||
return \IPS\Content\Api\GraphQL\TypeRegistry::comment();
|
||||
}
|
||||
else if ( $item instanceof \IPS\Content\Item )
|
||||
{
|
||||
return \IPS\Content\Api\GraphQL\TypeRegistry::item();
|
||||
}
|
||||
else if( $item instanceof \IPS\Node\Model )
|
||||
{
|
||||
return \IPS\Node\Api\GraphQL\TypeRegistry::node();
|
||||
}
|
||||
}
|
||||
]),
|
||||
'description' => 'The original item',
|
||||
'resolve' => function ($item) {
|
||||
return $item->object();
|
||||
}
|
||||
],
|
||||
'itemType' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'core_PromotedItem_Type',
|
||||
'values' => ['COMMENT', 'REVIEW', 'ITEM', 'NODE']
|
||||
]),
|
||||
'description' => "What kind of content is this item?",
|
||||
'resolve' => function ($item) {
|
||||
if ( $item->object() instanceof \IPS\Content\Comment )
|
||||
{
|
||||
return 'COMMENT';
|
||||
}
|
||||
else if ( $item->object() instanceof \IPS\Content\Review )
|
||||
{
|
||||
return 'REVIEW';
|
||||
}
|
||||
else if ( $item->object() instanceof \IPS\Content )
|
||||
{
|
||||
return 'ITEM';
|
||||
}
|
||||
else if ( $item->object() instanceof \IPS\Node\Model )
|
||||
{
|
||||
return 'NODE';
|
||||
}
|
||||
|
||||
print_r( $item->object() );
|
||||
exit;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'description' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => 'Promoted blurb provided by staff',
|
||||
'resolve' => function ($item) {
|
||||
$text = trim( $item->getText('internal', false) );
|
||||
|
||||
if( $text ){
|
||||
return $text;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
],
|
||||
'timestamp' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => 'Timestamp for when item was promoted',
|
||||
'resolve' => function ($item) {
|
||||
return $item->sent;
|
||||
}
|
||||
],
|
||||
'reputation' => [
|
||||
'type' => TypeRegistry::reputation(),
|
||||
'resolve' => function ($item) {
|
||||
$reactionClass = $item->objectReactionClass;
|
||||
return $reactionClass;
|
||||
}
|
||||
],
|
||||
'dataCount' => [
|
||||
'type' => new ObjectType([
|
||||
'name' => 'core_ourPicks_dataCount',
|
||||
'fields' => [
|
||||
'count' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'resolve' => function ($array) {
|
||||
return $array['count'];
|
||||
}
|
||||
],
|
||||
'words' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'resolve' => function ($array) {
|
||||
return $array['words'];
|
||||
}
|
||||
]
|
||||
]
|
||||
]),
|
||||
'resolve' => function ($item) {
|
||||
if( $item->objectDataCount ){
|
||||
return $item->objectDataCount;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Report reason type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 8 Jun 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReportReasonType for GraphQL API
|
||||
*/
|
||||
class _ReportReasonType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_ReportReason',
|
||||
'description' => 'Report reason type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Reason ID",
|
||||
'resolve' => function ($reason) {
|
||||
return $reason['id'];
|
||||
}
|
||||
],
|
||||
'reason' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Reason description",
|
||||
'resolve' => function ($reason) {
|
||||
return $reason['reason'];
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Report type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 14 Jun 2019
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReportType for GraphQL API
|
||||
*/
|
||||
class _ReportType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Report',
|
||||
'description' => 'Report type',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Report ID",
|
||||
'resolve' => function ($content) {
|
||||
if( $this->hasReported( $content ) ) {
|
||||
return $content->reportData['id'];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
],
|
||||
'hasReported' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Has the user reported this?",
|
||||
'resolve' => function ($content) {
|
||||
return $this->hasReported($content);
|
||||
}
|
||||
],
|
||||
'reportType' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "If the user has reported, the report type they made",
|
||||
'resolve' => function ($content) {
|
||||
if( $this->hasReported( $content ) ) {
|
||||
return $content->reportData['report_type'];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
],
|
||||
'reportDate' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "If the user has reported, the date of the report",
|
||||
'resolve' => function ($content) {
|
||||
if( $this->hasReported( $content ) ) {
|
||||
return $content->reportData['date_reported'];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
],
|
||||
'reportContent' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "If the user has reported, the report message if any",
|
||||
'resolve' => function ($content) {
|
||||
if( $this->hasReported( $content ) ) {
|
||||
return $content->reportData['report'];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user has reported this content
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasReported($content)
|
||||
{
|
||||
return ( $content->canReport( \IPS\Member::loggedIn() ) === 'report_err_already_reported' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Union type for search results
|
||||
* @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 22 Sep 2018
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Search result union type
|
||||
*/
|
||||
class _SearchResultType extends UnionType
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'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()
|
||||
],
|
||||
'resolveType' => function ($result) {
|
||||
if ( $result instanceof \IPS\Member )
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::member();
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\core\api\GraphQL\TypeRegistry::contentSearchResult();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Search Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 22 Sep 2018
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* SearchType for GraphQL API
|
||||
*/
|
||||
class _SearchType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Search',
|
||||
'description' => 'Search results',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'count' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'description' => "Total number of results",
|
||||
'resolve' => function ($search) {
|
||||
return $search['count'];
|
||||
}
|
||||
],
|
||||
'results' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::searchResult() ),
|
||||
'description' => "List of items in this stream",
|
||||
'resolve' => function ($search, $args, $context) {
|
||||
return $search['results'];
|
||||
}
|
||||
],
|
||||
'types' => [
|
||||
'type' => TypeRegistry::listOf( new ObjectType([
|
||||
'name' => 'core_search_types',
|
||||
'description' => "The available search types",
|
||||
'fields' => [
|
||||
'key' => TypeRegistry::string(),
|
||||
'lang' => TypeRegistry::string()
|
||||
],
|
||||
'resolveField' => function ($type, $args, $context, $info) {
|
||||
switch( $info->fieldName )
|
||||
{
|
||||
case 'key':
|
||||
return $type;
|
||||
break;
|
||||
case 'lang':
|
||||
return \IPS\Member::loggedIn()->language()->get( $type . '_pl' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
]) ),
|
||||
'resolve' => function () {
|
||||
return array_merge( array('core_members'), array_keys( \IPS\core\modules\front\search\search::contentTypes() ) );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Settings field Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* SettingsType for GraphQL API
|
||||
*/
|
||||
class _SettingsType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$config = [
|
||||
'name' => 'core_Settings',
|
||||
'description' => 'System settings',
|
||||
'fields' => function () {
|
||||
return [
|
||||
// Invidival settings are specified here to ensure that only whitelisted settings
|
||||
// are exposed via the API.
|
||||
|
||||
/* -------------------------------- */
|
||||
/* Basic system settings */
|
||||
'base_url' => [
|
||||
'type' => TypeRegistry::string()
|
||||
],
|
||||
'tags_enabled' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'tags_min' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
],
|
||||
'tags_max' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
],
|
||||
'tags_min_req' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'tags_len_min' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
],
|
||||
'tags_len_max' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
],
|
||||
'tags_open_system' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'site_online' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
],
|
||||
'site_offline_message' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
],
|
||||
'board_name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
],
|
||||
'reputation_enabled' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'reputation_highlight' => [
|
||||
'type' => TypeRegistry::int()
|
||||
],
|
||||
'reputation_show_profile' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'allow_reg' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'allowReg',
|
||||
'values' => ['NORMAL', 'FULL', 'REDIRECT', 'DISABLED']
|
||||
]),
|
||||
'resolve' => function ($val, $args, $context, $info) {
|
||||
return \strtoupper( \IPS\Login::registrationType() );
|
||||
}
|
||||
],
|
||||
'allow_reg_target' => [
|
||||
'type' => TypeRegistry::string()
|
||||
],
|
||||
'disable_anonymous' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'allow_result_view' => [
|
||||
'type' => TypeRegistry::boolean()
|
||||
],
|
||||
'geolocation_enabled' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'resolve' => function () {
|
||||
return \IPS\GeoLocation::enabled();
|
||||
}
|
||||
],
|
||||
'mobileHomeBlocks' => [
|
||||
'type' => TypeRegistry::listOf( TypeRegistry::string() ),
|
||||
'resolve' => function () {
|
||||
try
|
||||
{
|
||||
$parsed = json_decode( \IPS\Settings::i()->mobile_app_home_blocks_order, TRUE );
|
||||
$blocksToShow = array_filter( $parsed );
|
||||
|
||||
return array_keys( $blocksToShow );
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return array("new_content", "active_users", "our_picks", "popular_contributors");
|
||||
}
|
||||
}
|
||||
],
|
||||
'version' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'resolve' => function () {
|
||||
return \IPS\Application::load('core')->long_version;
|
||||
}
|
||||
],
|
||||
|
||||
/* -------------------------------- */
|
||||
/* Legal settings */
|
||||
'privacy_type' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'privacyType',
|
||||
'values' => ['INTERNAL', 'EXTERNAL', 'NONE']
|
||||
]),
|
||||
'resolve' => function ($val) {
|
||||
return mb_strtoupper( \IPS\Settings::i()->privacy_type );
|
||||
}
|
||||
],
|
||||
'privacy_text' => [
|
||||
'type' => TypeRegistry::richText(),
|
||||
'resolve' => function () {
|
||||
return \IPS\Member::loggedIn()->language()->get('privacy_text_value');
|
||||
}
|
||||
],
|
||||
'privacy_link' => [
|
||||
'type' => TypeRegistry::string()
|
||||
],
|
||||
'reg_rules' => [
|
||||
'type' => TypeRegistry::richText(),
|
||||
'resolve' => function () {
|
||||
return \IPS\Member::loggedIn()->language()->get('reg_rules_value');
|
||||
}
|
||||
],
|
||||
'guidelines_type' => [
|
||||
'type' => TypeRegistry::eNum([
|
||||
'name' => 'guidelinesType',
|
||||
'values' => ['INTERNAL', 'EXTERNAL', 'NONE']
|
||||
]),
|
||||
'resolve' => function ($val) {
|
||||
return mb_strtoupper( \IPS\Settings::i()->gl_type );
|
||||
}
|
||||
],
|
||||
'guidelines_text' => [
|
||||
'type' => TypeRegistry::richText(),
|
||||
'resolve' => function ($val) {
|
||||
return \IPS\Member::loggedIn()->language()->get('guidelines_value');
|
||||
}
|
||||
],
|
||||
'guidelines_link' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'resolve' => function ($val) {
|
||||
return \IPS\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();
|
||||
}
|
||||
],
|
||||
|
||||
/* -------------------------------- */
|
||||
/* Upload settings */
|
||||
'allowedFileTypes' => [
|
||||
'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();
|
||||
}
|
||||
],
|
||||
'chunkingSupported' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "If chunking is supported",
|
||||
'resolve' => function() {
|
||||
$storageClass = \IPS\File::getClass( 'core_Attachment' );
|
||||
return $storageClass::$supportsChunking;
|
||||
}
|
||||
],
|
||||
'maxChunkSize' => [
|
||||
'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();
|
||||
}
|
||||
],
|
||||
|
||||
/* -------------------------------- */
|
||||
/* Report settings */
|
||||
'automoderationEnabled' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Whether the automoderation feature is enabled",
|
||||
'resolve' => function () {
|
||||
return \IPS\Settings::i()->automoderation_enabled;
|
||||
}
|
||||
],
|
||||
'reportReasons' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::reportReason() ),
|
||||
'deescription' => "The available reasons for reporting content",
|
||||
'resolve' => function () {
|
||||
$options = array();
|
||||
|
||||
$options[] = array(
|
||||
'id' => \IPS\core\Reports\Report::TYPE_MESSAGE,
|
||||
'reason' => \IPS\Member::loggedIn()->language()->addToStack('report_message_item')
|
||||
);
|
||||
|
||||
foreach( \IPS\core\Reports\Types::roots() as $type )
|
||||
{
|
||||
$options[] = array(
|
||||
'id' => $type->id,
|
||||
'reason' => $type->_title
|
||||
);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
]
|
||||
];
|
||||
},
|
||||
'resolveField' => function ($val, $args, $context, $info) {
|
||||
$setting = $info->fieldName;
|
||||
try
|
||||
{
|
||||
return \IPS\Settings::i()->$setting;
|
||||
}
|
||||
catch(\Exception $error)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Stream Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* StreamType for GraphQL API
|
||||
*/
|
||||
class _StreamType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Stream',
|
||||
'description' => 'Activity streams',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => TypeRegistry::id(),
|
||||
'description' => "Stream ID",
|
||||
'resolve' => function ($stream) {
|
||||
return $stream->id;
|
||||
}
|
||||
],
|
||||
'title' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Stream title",
|
||||
'resolve' => function ($stream) {
|
||||
if( !$stream->title ) {
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( "stream_title_{$stream->id}" );
|
||||
}
|
||||
return $stream->title;
|
||||
}
|
||||
],
|
||||
'member' => [
|
||||
'type' => \IPS\core\api\GraphQL\TypeRegistry::member(),
|
||||
'description' => "Stream owner, if applicable",
|
||||
'resolve' => function ($stream) {
|
||||
return ( $stream->member ) ? \IPS\Member::load( $stream->member ) : null;
|
||||
}
|
||||
],
|
||||
'isDefault' => [
|
||||
'type' => TypeRegistry::boolean(),
|
||||
'description' => "Is this a default stream?",
|
||||
'resolve' => function ($stream) {
|
||||
return !( $stream->member );
|
||||
}
|
||||
],
|
||||
'items' => [
|
||||
'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::contentSearchResult() ),
|
||||
'description' => "List of items in this stream",
|
||||
'args' => [
|
||||
'offset' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 0
|
||||
],
|
||||
'limit' => [
|
||||
'type' => TypeRegistry::int(),
|
||||
'defaultValue' => 25
|
||||
],
|
||||
'club' => TypeRegistry::int(),
|
||||
'unread' => TypeRegistry::boolean(),
|
||||
|
||||
],
|
||||
'resolve' => function ($stream, $args, $context) {
|
||||
return self::items( $stream, $args, $context );
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve items
|
||||
*
|
||||
* @param \IPS\core\Stream
|
||||
* @param array Arguments passed from resolver
|
||||
* @return array
|
||||
*/
|
||||
protected static function items( $stream, $args, $context )
|
||||
{
|
||||
/* Clubs */
|
||||
if( isset( $args['club'] ) )
|
||||
{
|
||||
$stream->clubs = $args['club'];
|
||||
}
|
||||
|
||||
/* Unread */
|
||||
if( isset( $args['unread'] ) )
|
||||
{
|
||||
if( ( $args['unread'] === TRUE && $stream->read !== 'unread' ) || ( $args['unread'] === FALSE && $stream->read === 'unread' ) )
|
||||
{
|
||||
$stream->read = ( $args['unread'] ) ? 'unread' : 'all';
|
||||
}
|
||||
}
|
||||
|
||||
/* Build the query */
|
||||
$query = $stream->query( \IPS\Member::loggedIn() );
|
||||
|
||||
// Get page
|
||||
// We don't know the count at this stage, so figure out the page number from
|
||||
// our offset/limit
|
||||
$page = 1;
|
||||
$offset = max( $args['offset'], 0 );
|
||||
$limit = min( $args['limit'], 50 );
|
||||
|
||||
if( $offset > 0 )
|
||||
{
|
||||
$page = floor( $offset / $limit ) + 1;
|
||||
}
|
||||
|
||||
$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 ) );
|
||||
|
||||
/* Load data we need like the authors, etc */
|
||||
$results->init();
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Tag Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 7 May 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* TagType for GraphQL API
|
||||
*/
|
||||
class _TagType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_Tag',
|
||||
'description' => 'A community tag',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Tag name",
|
||||
'resolve' => function ($tag) {
|
||||
return $tag;
|
||||
}
|
||||
],
|
||||
'url' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Tag URL",
|
||||
'resolve' => function ($tag) {
|
||||
$_tag = urlencode($tag);
|
||||
return \IPS\Http\Url::internal( "app=core&module=search&controller=search&tags={$_tag}", 'front', 'tags' );
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief GraphQL: Upload progress Type
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 27 Mar 2020
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\core\api\GraphQL\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use IPS\Api\GraphQL\TypeRegistry;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* UploadProgressType for GraphQL API
|
||||
*/
|
||||
class _UploadProgressType extends ObjectType
|
||||
{
|
||||
/**
|
||||
* Get object type
|
||||
*
|
||||
* @return ObjectType
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'name' => 'core_UploadProgress',
|
||||
'description' => 'Upload progress',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "The attachment's original filename (not necessarily what is actually stored on disk)",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['name'];
|
||||
}
|
||||
],
|
||||
'ref' => [
|
||||
'type' => TypeRegistry::string(),
|
||||
'description' => "Upload ref",
|
||||
'resolve' => function( $attachment ) {
|
||||
return $attachment['ref'];
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user