Invision Power Services, Inc. * @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\ListOfType; use IPS\Api\GraphQL\TypeRegistry; use IPS\Content\Search\Mysql\Query; use IPS\core\api\GraphQL\Types\MessengerConversationType; use IPS\Db; use IPS\Member; use IPS\Patterns\ActiveRecordIterator; use UnderflowException; use function count; use function defined; use function in_array; use function is_null; /* To prevent PHP errors (extending class does not exist) revealing path */ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } /** * Messenger conversations query for GraphQL API */ class MessengerConversations { /* * @brief Query description */ public static string $description = "Returns a list of messenger conversations"; /* * Query arguments */ public function args(): array { 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' => 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 * * @return ListOfType */ public function type() : ListOfType { return TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::messengerConversation() ); } /** * Resolves this query * * @param mixed $val Value passed into this resolver * @param array $args Arguments * @param array $context Context values * @param mixed $info * @return ActiveRecordIterator|array */ public function resolve( mixed $val, array $args, array $context, mixed $info ) : ActiveRecordIterator|array { // Start to build the query to fetch messages $where = array( array( 'core_message_topic_user_map.map_user_id=? AND core_message_topic_user_map.map_user_active=1', Member::loggedIn()->member_id ) ); $orderBy = 'mt_' . $args['orderBy']; $offset = max( $args['offset'], 0 ); $limit = min( $args['limit'], 50 ); $iterator = NULL; // Filters if( !is_null( $args['filter'] ) ) { 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 = 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 = Db::i()->prefix; if ( !is_null( $args['searchIn'] ) ) { if ( in_array( 'TOPIC', $args['searchIn'] ) ) { $query[] = Query::matchClause( "core_message_topics.mt_title", $args['search'], '+', FALSE ); } if ( in_array( 'POST', $args['searchIn'] ) ) { $query[] = 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 '" . 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 '" . Db::i()->escape_string( $args['search'] ) . "%') )"; } if ( count( $query ) ) { $where[] = array( '(' . implode( ' OR ', $query ) . ')' ); } } /* Get a count */ try { $count = Db::i()->select( 'COUNT(*)', 'core_message_posts', $where, NULL, NULL, 'msg_topic_id' ) ->join( 'core_message_topics', 'core_message_posts.msg_topic_id=core_message_topics.mt_id' ) ->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 = 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 = 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 = 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 ActiveRecordIterator( $iterator, 'IPS\core\Messenger\Conversation' ); } return array(); } }