Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @since 5 Jul 2013 */ namespace IPS\core\modules\front\messaging; /* To prevent PHP errors (extending class does not exist) revealing path */ use BadMethodCallException; use Exception; use InvalidArgumentException; use IPS\Application; use IPS\Content\Controller; use IPS\Content\Search\Mysql\Query; use IPS\core\Alerts\Alert; use IPS\core\DataLayer; use IPS\core\Messenger\Conversation; use IPS\DateTime; use IPS\Db; use IPS\Helpers\Form; use IPS\Helpers\Form\Select; use IPS\Helpers\Form\Text; use IPS\Http\Url; use IPS\Member; use IPS\Notification; use IPS\Output; use IPS\Request; use IPS\Session; use IPS\Theme; use LogicException; use OutOfRangeException; use UnderflowException; use function count; use function defined; use function in_array; use function intval; use function is_array; use function is_null; if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } /** * Messenger */ class messenger extends Controller { /** * [Content\Controller] Class */ protected static string $contentModel = 'IPS\core\Messenger\Conversation'; /** * Execute * * @return void */ public function execute() : void { if ( !Member::loggedIn()->member_id ) { Output::i()->error( 'no_module_permission_guest', '2C137/1', 403, '' ); } if ( Member::loggedIn()->members_disable_pm == 2 ) { Output::i()->error( 'messenger_disabled', '2C137/9', 403, '' ); } Output::i()->sidebar['enabled'] = FALSE; if ( !Request::i()->isAjax() ) { Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) ); Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) ); Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front_model_messages.js', 'core' ) ); Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_messages.js', 'core' ) ); Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/messaging.css' ) ); } /* Set Session Location */ Session::i()->setLocation( Url::internal( 'app=core&module=messaging&controller=messenger', NULL, 'messaging' ), array(), 'loc_using_messenger' ); parent::execute(); } /** * Messenger * * @return mixed */ protected function manage() : mixed { $baseUrl = Url::internal( "app=core&module=messaging&controller=messenger", 'front', 'messaging' ); $alert = NULL; /* Get folders */ $folders = array( 'myconvo' => Member::loggedIn()->language()->addToStack('messenger_folder_inbox') ); if ( Member::loggedIn()->pconversation_filters ) { $folders = $folders + array_filter( json_decode( Member::loggedIn()->pconversation_filters, TRUE ) ); } /* Are we looking at a specific folder? */ if ( isset( $folders[ Request::i()->folder ] ) ) { $baseUrl = $baseUrl->setQueryString( 'folder', Request::i()->folder ); $folder = Request::i()->folder; } else { $folder = NULL; } /* 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( Db::i()->select( 'map_folder_id, count(*) as count', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', Member::loggedIn()->member_id ), NULL, NULL, 'map_folder_id' ) ); $folderCounts = array(); foreach( $counts AS $k => $count ) { $folderCounts[$count['map_folder_id']] = $count['count']; } /* Are we looking at a message? */ $conversation = NULL; if ( Request::i()->id ) { try { $conversation = Conversation::loadAndCheckPerms( Request::i()->id ); /* If this message triggered a PM popup and we view it directly, it will no longer be marked as unread but msg_show_notification for the member will still be set, causing the next oldest PM to show in a popup erroneously. If this is the latest unread PM, reset msg_show_notification now to prevent the popup from showing. */ if( Member::loggedIn()->msg_show_notification ) { $latestConversation = NULL; try { $latestConversation = Db::i()->select( 'map_topic_id', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1 AND map_has_unread=1 AND map_ignore_notification=0', Member::loggedIn()->member_id ), 'map_last_topic_reply DESC' )->first(); } catch ( UnderflowException $e ) { } if( $latestConversation == $conversation->id ) { Member::loggedIn()->msg_show_notification = FALSE; Member::loggedIn()->save(); } } Db::i()->update( 'core_message_topic_user_map', array( 'map_read_time' => time(), 'map_has_unread' => 0 ), array( 'map_user_id=? AND map_topic_id=?', Member::loggedIn()->member_id, $conversation->id ) ); Conversation::rebuildMessageCounts( Member::loggedIn() ); if ( Request::i()->isAjax() and Request::i()->getRow ) { $row = Db::i()->select( 'core_message_topic_user_map.*, core_message_topics.*', 'core_message_posts', array( 'mt_id=?', $conversation->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 AND core_message_topic_user_map.map_user_id=' . intval( Member::loggedIn()->member_id ) )->first(); $row['last_message'] = Conversation::load( $row['mt_id'] )->comments( 1, 0, 'date', 'desc' ); $row['participants'] = Conversation::load( $row['mt_id'] )->participantBlurb(); Output::i()->json( Theme::i()->getTemplate( 'messaging' )->messageListRow( $row, !Request::i()->_fromMenu, $folders ) ); } Output::i()->title = $conversation->title; $baseUrl = $baseUrl->setQueryString( 'id', Request::i()->id ); if ( isset( Request::i()->page ) and $conversation->replies ) { $maxPages = ceil( $conversation->replies / $conversation::getCommentsPerPage() ); if ( Request::i()->page > $maxPages ) { Output::i()->redirect( $baseUrl->setPage( 'page', $maxPages ) ); } } /* We check isset() on the map because checking message from a report means no map will be returned */ if ( $folder === NULL AND isset( $conversation->map['map_folder_id'] ) ) { $folder = $conversation->map['map_folder_id']; } } catch ( OutOfRangeException $e ) { Output::i()->error( 'node_error', '2C137/2', 403, '' ); } if ( isset( Request::i()->latest ) ) { $maps = $conversation->maps(); $message = NULL; if ( isset( $maps[ Member::loggedIn()->member_id ] ) and $maps[ Member::loggedIn()->member_id ]['map_read_time'] ) { $message = $conversation->comments( 1, NULL, 'date', 'asc', NULL, NULL, DateTime::ts( $maps[ Member::loggedIn()->member_id ]['map_read_time'] ) ); } /** if we don't have a unread comment, redirect to the last comment */ if ( !$message ) { $message = $conversation->comments( 1, NULL, 'date', 'desc', NULL, NULL ); } if ( $message ) { Output::i()->redirect( $message->url() ); } } if( $conversation->alert ) { try { $alert = Alert::load( $conversation->alert ); if( ! $alert->forMember( Member::loggedIn() ) and $alert->author()->member_id != Member::loggedIn()->member_id ) { $alert = NULL; } } catch ( OutOfRangeException $e ) {} } } /* Default folder */ if ( $folder === NULL ) { $folder = 'myconvo'; } if ( !isset( Request::i()->id ) ) { Output::i()->title = $folders[ $folder ]; } /* Do we need a filter? */ $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 ) ); if ( Request::i()->filter == 'mine' ) { $where[] = array( 'core_message_topic_user_map.map_is_starter=1' ); } elseif( Request::i()->filter == 'not_mine' ) { $where[] = array( 'core_message_topic_user_map.map_is_starter=0' ); } elseif( Request::i()->filter == 'read' ) { $where[] = array( 'core_message_topic_user_map.map_has_unread=0' ); } elseif( Request::i()->filter == 'not_read' ) { $where[] = array( 'core_message_topic_user_map.map_has_unread=1' ); } /* Add a folder filter, if we are not coming from the Messages menu */ if ( !isset( Request::i()->_fromMenu ) ) { if ( !is_null( $folder ) AND isset( $folders[ $folder ] ) ) { $where[] = array( 'core_message_topic_user_map.map_folder_id=?', $folder ); } else { $where[] = array( 'core_message_topic_user_map.map_folder_id=?', 'myconvo' ); } } /* If we're searching, get the results */ $perPage = 25; $iterator = array(); if ( Request::i()->q ) { $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 ( isset( Request::i()->search ) and $searchValues = Request::i()->valueFromArray('search') ) { if ( isset( $searchValues['topic'] ) or isset( $searchValues['post'] ) ) { $fulltext = []; if ( isset( $searchValues['topic'] ) ) { $fulltext[] = Query::matchClause( "core_message_topics.mt_title", Request::i()->q, '+', FALSE ); } if ( isset( $searchValues['post'] ) ) { $fulltext[] = Query::matchClause( "core_message_posts.msg_post", Request::i()->q, '+', FALSE ); } $query[] = "core_message_posts.msg_id IN( SELECT msg_id FROM {$prefix}core_message_posts as core_message_posts LEFT JOIN {$prefix}core_message_topics as core_message_topics ON core_message_posts.msg_topic_id=core_message_topics.mt_id WHERE (" . implode( " OR ", $fulltext ) . ") )"; } if ( isset( $searchValues['recipient'] ) ) { $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( Request::i()->q ) . "%' ) )"; } if ( isset( $searchValues['sender'] ) ) { $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( Request::i()->q ) . "%') )"; } if ( count( $query ) ) { $where[] = array( '(' . implode( ' OR ', $query ) . ')' ); } } /* Get a count */ try { $count = Db::i()->select( 'COUNT( DISTINCT( msg_topic_id ) )', 'core_message_posts', $where ) ->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 */ $sortColumn = ( in_array( Request::i()->sortBy, array( 'mt_last_post_time', 'mt_start_time', 'mt_replies' ) ) ? Request::i()->sortBy : 'mt_last_post_time' ); $iterator = Db::i()->select( 'core_message_posts.msg_topic_id', 'core_message_posts', $where, $sortColumn . ' DESC', array( ( intval( Request::i()->listPage ?: 1 ) - 1 ) * $perPage, $perPage ), array( 'msg_topic_id', $sortColumn ) )->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 ) ) . ')' ), $sortColumn . ' DESC' )->join( 'core_message_topics', 'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id' ); } } else { /* Get a count */ $count = Db::i()->select( 'COUNT(*)', 'core_message_topic_user_map', $where )->first(); /* Performance: if $count is 0, don't bother selecting ... it's a wasted query */ if( $count ) { if ( $alert = Alert::getAlertCurrentlyFilteringMessages() ) { $where[] = array( 'core_message_topics.mt_alert=?', $alert->id ); } /* Get iterator */ $iterator = Db::i()->select( 'core_message_topic_user_map.*, core_message_topics.*', 'core_message_topic_user_map', $where, ( in_array( Request::i()->sortBy, array( 'mt_last_post_time', 'mt_start_time', 'mt_replies' ) ) ? Request::i()->sortBy : 'mt_last_post_time' ) . ' DESC', array( ( intval( Request::i()->listPage ?: 1 ) - 1 ) * $perPage, $perPage ) )->join( 'core_message_topics', 'core_message_topic_user_map.map_topic_id=core_message_topics.mt_id' ); } } /* Get the map data in one query to avoid querying it separately for each message */ $messageIds = array(); $userIds = array(); $maps = array(); $results = $count ? iterator_to_array( $iterator ) : array(); foreach( $results as $row ) { $messageIds[] = $row['mt_id']; } foreach( Db::i()->select( '*', 'core_message_topic_user_map', array( Db::i()->in( 'map_topic_id', $messageIds ) ) )->setKeyField( 'map_user_id' ) as $mapRow ) { $maps[ $mapRow['map_topic_id'] ][ $mapRow['map_user_id'] ] = $mapRow; $userIds[ $mapRow['map_user_id'] ] = $mapRow['map_user_id']; } if( count( $userIds ) ) { foreach( Db::i()->select( 'member_id, name', 'core_members', array( Db::i()->in( 'member_id', $userIds ) ) ) as $member ) { if ( $member['member_id'] === Member::loggedIn()->member_id ) { $member['name'] = Member::loggedIn()->language()->addToStack('participant_you_lower'); } Conversation::$participantMembers[ $member['member_id'] ] = $member['name']; } } /* Build the message list */ $conversations = array(); foreach ( $results as $row ) { Conversation::constructFromData( $row )->maps = $maps[ $row['mt_id'] ]; $row['last_message'] = Conversation::load( $row['mt_id'] )->comments( 1, 0, 'date', 'desc' ); $row['participants'] = Conversation::load( $row['mt_id'] )->participantBlurb(); $conversations[ $row['mt_id'] ] = $row; } /* Note the last time we looked at the message list */ Member::loggedIn()->msg_count_reset = time(); Conversation::rebuildMessageCounts( Member::loggedIn() ); /* Display */ $listUrl = $baseUrl; $baseUrl = $baseUrl->setQueryString( '_list', 1 ); if( isset( Request::i()->filter ) ) { $baseUrl = $baseUrl->setQueryString( 'filter', Request::i()->filter ); } if( isset( Request::i()->q ) ) { $baseUrl = $baseUrl->setQueryString( 'q', Request::i()->q ); $baseUrl = $baseUrl->setQueryString( 'search', Request::i()->search ); } $pagination = Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, ceil( $count / $perPage ), ( Request::i()->listPage ?: 1 ), $perPage, TRUE, 'listPage' ); if ( Request::i()->isAjax() ) { if( Request::i()->id ) { Output::i()->output = Theme::i()->getTemplate('messaging')->conversation( $conversation, $folders, $alert ); } elseif( Request::i()->q ) { /* If we are viewing > page 1 we need to return HTML instead of an object, since the infinite scroll library will have taken over now and just wants HTML */ if( Request::i()->listPage AND Request::i()->listPage > 1 ) { Output::i()->sendOutput( Theme::i()->getTemplate('messaging')->messageListRows( $conversations, $pagination, FALSE, $folders ) ); } else { Output::i()->json( array( 'data' => Theme::i()->getTemplate('messaging')->messageListRows( $conversations, $pagination, FALSE, $folders ), 'pagination' => $pagination ) ); } } elseif( Request::i()->overview ) { Output::i()->json( array( 'data' => Theme::i()->getTemplate('messaging')->messageListRows( $conversations, $pagination, !Request::i()->_fromMenu, $folders ), 'pagination' => $pagination, 'listBaseUrl' => $listUrl->setQueryString( array( 'sortBy' => Request::i()->sortBy, 'filter' => Request::i()->filter ) )->stripQueryString( 'id' ) ) ); } else { Output::i()->sendOutput( Theme::i()->getTemplate('messaging')->messageListRows( $conversations, $pagination, TRUE, $folders ) ); } } else { if ( DataLayer::enabled() and $conversation ) { foreach ( $conversation->getDataLayerProperties() as $key => $value ) { DataLayer::i()->addContextProperty( $key, $value ); } } Output::i()->output = Theme::i()->getTemplate('messaging')->template( $folder, $folders, $folderCounts, $conversations, $pagination, $conversation, $baseUrl, ( $conversation ? 'messenger_convo' : 'messenger' ), ( Request::i()->sortBy ? htmlspecialchars( Request::i()->sortBy, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ): 'mt_last_post_time' ), ( Request::i()->filter ? htmlspecialchars( Request::i()->filter, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ) : '' ), $alert ); } return null; } /** * Compose * * @return void */ protected function compose() : void { Output::i()->title = Member::loggedIn()->language()->addToStack('compose_new'); if( Request::i()->alert ) { try { $alert = Alert::load( Request::i()->alert ); if( $alert->forMember( Member::loggedIn() ) ) { $form = $alert->getNewConversationForm(); Output::i()->title = Member::loggedIn()->language()->addToStack('alert_modal_title', FALSE, array( 'sprintf' => array( $alert->title ) ) ); } } catch ( OutOfRangeException $e ) {} } if ( empty( $form) ) { $form = Conversation::create(); $form->class = 'ipsForm--vertical ipsForm--compose-message'; } if( Request::i()->isAjax() ) { Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ); } else { Output::i()->output = Theme::i()->getTemplate('messaging')->submitForm( Member::loggedIn()->language()->addToStack('compose_new'), $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ) ); } } /** * Move messages form * * @return void */ protected function moveForm() : void { Session::i()->csrfCheck(); $folders = json_decode( Member::loggedIn()->pconversation_filters, TRUE ) ?? array(); array_walk( $folders, function( &$val ) { $val = htmlspecialchars( $val, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8' ); } ); $form = new Form; $form->add( new Select( 'move_message_to', NULL, FALSE, array( 'options' => array( 'myconvo' => Member::loggedIn()->language()->addToStack('inbox') ) + $folders, 'parse' => 'normal' ) ) ); if ( $values = $form->values() ) { $ids = explode( ',', Request::i()->ids ); foreach( $ids as $id ) { try { $conversation = Conversation::loadAndCheckPerms( $id ); $conversation->moveConversation( $values['move_message_to'] ); } catch ( OutOfRangeException ) { continue; } } Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger&folder=' . Request::i()->move_message_to, 'front', 'messaging' ) ); } Output::i()->title = 'messenger_move'; Output::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ); } /** * Add Folder * * @return void */ protected function addFolder() : void { Session::i()->csrfCheck(); $form = new Form; $form->add( new Text( 'messenger_add_folder_name', NULL, TRUE ) ); if ( $values = $form->values() ) { $folders = json_decode( Member::loggedIn()->pconversation_filters, TRUE ) ?? array(); $folders[] = $values['messenger_add_folder_name']; Member::loggedIn()->pconversation_filters = json_encode( $folders ); Member::loggedIn()->save(); if ( Request::i()->isAjax() ) { $keys = array_keys( $folders ); Output::i()->json( array( 'folderName' => $values['messenger_add_folder_name'], 'key' => array_pop( $keys ) ) ); } else { Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } } $form->class = 'ipsForm--vertical ipsForm--add-inbox-folder'; $formHtml = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ); Output::i()->output = Output::i()->output = Theme::i()->getTemplate( 'messaging' )->folderForm( 'add', $formHtml ); } /** * Mark Folder Read * * @return void */ protected function readFolder() : void { Session::i()->csrfCheck(); Db::i()->update( 'core_message_topic_user_map', array( 'map_has_unread' => FALSE ), array( 'map_user_id=? AND map_folder_id=?', Member::loggedIn()->member_id, Request::i()->folder ) ); Conversation::rebuildMessageCounts( Member::loggedIn() ); Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } /** * Delete Folder * * @return void */ protected function deleteFolder() : void { Session::i()->csrfCheck(); /* Make sure the user confirmed the deletion */ Request::i()->confirmedDelete(); /* Make sure the user isn't playing with the URL to do strange things */ $folders = json_decode( Member::loggedIn()->pconversation_filters, TRUE ) ?? array(); if( Request::i()->folder == 'myconvo' OR !isset( $folders[ Request::i()->folder ] ) ) { Output::i()->error( 'messenger_cannot_delete_folder', '3C137/C', 404, '' ); } Db::i()->update( 'core_message_topic_user_map', array( 'map_user_active' => FALSE, 'map_left_time' => time() ), array( 'map_user_id=? AND map_folder_id=?', Member::loggedIn()->member_id, Request::i()->folder ) ); unset( $folders[ Request::i()->folder ] ); Member::loggedIn()->pconversation_filters = json_encode( $folders ); Member::loggedIn()->save(); Conversation::rebuildMessageCounts( Member::loggedIn() ); if ( Request::i()->isAjax() ) { Output::i()->json( $this->_getNewTotals() ); } else { Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } } /** * Empty Folder * * @return void */ protected function emptyFolder() : void { Session::i()->csrfCheck(); Db::i()->update( 'core_message_topic_user_map', array( 'map_user_active' => FALSE, 'map_left_time' => time() ), array( 'map_user_id=? AND map_folder_id=?', Member::loggedIn()->member_id, Request::i()->folder ) ); Conversation::rebuildMessageCounts( Member::loggedIn() ); if ( Request::i()->isAjax() ) { Output::i()->json( $this->_getNewTotals() ); } else { Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } } /** * Rename Folder * * @return void */ protected function renameFolder() : void { Session::i()->csrfCheck(); $folders = json_decode( Member::loggedIn()->pconversation_filters, TRUE ); $form = new Form; $form->add( new Text( 'messenger_add_folder_name', $folders[ Request::i()->folder ], TRUE ) ); if ( $values = $form->values() ) { $folders[ Request::i()->folder ] = $values['messenger_add_folder_name']; Member::loggedIn()->pconversation_filters = json_encode( $folders ); Member::loggedIn()->save(); if ( Request::i()->isAjax() ) { Output::i()->json( array( 'folderName' => $values['messenger_add_folder_name'], 'key' => Request::i()->folder ) ); } else { Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } } $form->class = 'ipsForm--vertical ipsForm--rename-inbox-folder'; $formHtml = $form->customTemplate( array( Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) ); Output::i()->output = Output::i()->output = Theme::i()->getTemplate( 'messaging' )->folderForm( 'rename', $formHtml ); } /** * Block a participant * * @return void */ protected function blockParticipant() : void { Session::i()->csrfCheck(); try { $conversation = Conversation::loadAndCheckPerms( Request::i()->id ); if ( $conversation->starter_id != Member::loggedIn()->member_id or Request::i()->member == $conversation->starter_id ) { throw new BadMethodCallException; } $conversation->deauthorize( Member::load( Request::i()->member ), TRUE ); if ( Request::i()->isAjax() ) { $thisUser = array(); foreach ( $conversation->maps( TRUE ) as $map ) { if( $map['map_user_id'] == Request::i()->member ) { $thisUser = $map; } } Output::i()->output = Output::i()->output = Theme::i()->getTemplate( 'messaging' )->participant( $thisUser, $conversation ); } else { Output::i()->redirect( $conversation->url() ); } } catch ( LogicException ) { Output::i()->error( 'no_module_permission', '2C137/4', 403, '' ); } } /** * Unblock / Add a participant * * @return void */ protected function addParticipant() : void { Session::i()->csrfCheck(); try { $conversation = Conversation::loadAndCheckPerms( Request::i()->id ); $members = array(); $ids = array(); $failed = 0; if ( isset( Request::i()->member_names ) ) { foreach ( explode( "\n", Request::i()->member_names ) as $name ) { /* We have to html_entity_decode because the javascript code sends the encoded name here */ $memberToAdd = Member::load( html_entity_decode( $name, ENT_QUOTES, 'UTF-8' ), 'name' ); if ( $memberToAdd->member_id ) { $members[] = $memberToAdd; } else { $failed++; } } } else { $memberToAdd = Member::load( Request::i()->member ); if ( $memberToAdd->member_id ) { $members[] = $memberToAdd; } else { $failed++; } } /* Check member limit */ if ( Member::loggedIn()->group['g_max_mass_pm'] !== -1 AND $conversation->to_count + count( $members ) > Member::loggedIn()->group['g_max_mass_pm'] ) { Output::i()->error( 'messenger_too_many_recipients', '3C137/D', 403, '' ); } /* If we failed to load any members, error out if we're not ajaxing */ if ( $failed > 0 && !Request::i()->isAjax() ) { Output::i()->error( 'form_member_bad', '2C137/5', 403, '' ); } $maps = $conversation->maps( TRUE ); /* Authorize each of the members */ foreach ( $members as $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 InvalidArgumentException( Member::loggedIn()->language()->addToStack('messenger_member_left', FALSE, array( 'sprintf' => array( $member->name ) ) ) ); } if ( !$conversation::memberCanReceiveNewMessage( $member, Member::loggedIn(), 'new' ) ) { throw new InvalidArgumentException( Member::loggedIn()->language()->addToStack('meesnger_err_bad_recipient', FALSE, array( 'sprintf' => array( $member->name ) ) ) ); } $maps = $conversation->authorize( $member ); $ids[] = $member->member_id; $notification = new Notification( Application::load('core'), 'private_message_added', $conversation, array( $conversation, Member::loggedIn() ) ); $notification->recipients->attach( $member ); $notification->send(); } /* Build the HTML for each new member */ $memberHTML = array(); foreach ( $maps as $map ) { if( in_array( $map['map_user_id'], $ids ) ) { $memberHTML[ $map['map_user_id'] ] = Theme::i()->getTemplate( 'messaging' )->participant( $map, $conversation ); } } if ( Request::i()->isAjax() ) { Output::i()->json( array( 'members' => $memberHTML, 'failed' => $failed ) ); } else { Output::i()->redirect( $conversation->url() ); } } catch ( InvalidArgumentException $e ) { if ( Request::i()->isAjax() ) { Output::i()->json( array( 'error' => $e->getMessage() ), 403 ); } else { Output::i()->error( $e->getMessage(), '2C137/B', 403, '' ); } } catch ( LogicException $e ) { Output::i()->error( 'no_module_permission', '2C137/5', 403, '' ); } } /** * Turn notifications on/off * * @return void */ protected function notifications() : void { Session::i()->csrfCheck(); try { $conversation = Conversation::loadAndCheckPerms( Request::i()->id ); Db::i()->update( 'core_message_topic_user_map', array( 'map_ignore_notification' => !Request::i()->status ), array( 'map_user_id=? AND map_topic_id=?', Member::loggedIn()->member_id, $conversation->id ) ); Output::i()->redirect( $conversation->url() ); } catch ( OutOfRangeException $e ) { Output::i()->error( 'no_module_permission', '2C137/6', 403, '' ); } } /** * Move a conversation from one folder to another * * @return void */ protected function move(): void { Session::i()->csrfCheck(); try { $conversation = Conversation::loadAndCheckPerms( Request::i()->id ); $conversation->moveConversation( Request::i()->to ); if ( Request::i()->isAjax() ) { Output::i()->json( $this->_getNewTotals() ); } else { Output::i()->redirect( $conversation->url() ); } } catch ( OutOfRangeException $e ) { Output::i()->error( 'no_module_permission', '2C137/8', 403, '' ); } } /** * Leave the conversation * * @return void */ protected function leaveConversation() : void { Session::i()->csrfCheck(); try { $ids = Request::i()->id; if ( !is_array( Request::i()->id ) ) { $ids = array( $ids ); } foreach ( $ids as $id ) { try { $conversation = Conversation::loadAndCheckPerms( $id ); $conversation->deauthorize( Member::loggedIn() ); } catch ( Exception $e ) {} } if ( Request::i()->isAjax() ) { Output::i()->json( array_merge( array( 'result' => 'success' ), $this->_getNewTotals() ) ); } else { Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ) ); } } catch ( OutOfRangeException $e ) { Output::i()->error( 'no_module_permission', '2C137/7', 403, '' ); } } /** * Enable Messenger * * @return void */ protected function enableMessenger() : void { Session::i()->csrfCheck(); Member::loggedIn()->members_disable_pm = 0; Member::loggedIn()->save(); Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ), 'messenger_enabled' ); } /** * Disable Messenger * * @return void */ protected function disableMessenger() : void { Session::i()->csrfCheck(); Member::loggedIn()->members_disable_pm = 1; Member::loggedIn()->save(); Output::i()->redirect( Url::internal( 'app=core&module=messaging&controller=messenger', 'front', 'messaging' ), 'messenger_disabled' ); } /** * Get user conversation storage data * * @return array */ protected function _getNewTotals() : array { /* Get folders */ $folders = array( 'myconvo' => Member::loggedIn()->language()->addToStack('messenger_folder_inbox') ); if ( Member::loggedIn()->pconversation_filters ) { $folders = array_merge( $folders, json_decode( Member::loggedIn()->pconversation_filters, TRUE ) ); } /* What are our folder counts? */ $counts = iterator_to_array( Db::i()->select( 'map_folder_id, count(*) as _count', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', Member::loggedIn()->member_id ), NULL, NULL, 'map_folder_id' )->setKeyField( 'map_folder_id' )->setValueField( '_count' ) ); /* Fill in for the empty folders */ foreach ( $folders as $id => $name ) { if( !isset( $counts[ $id ] ) ) { $counts[ $id ] = 0; } } return array( 'quotaText' => Member::loggedIn()->language()->addToStack( 'messenger_quota', FALSE, array( 'sprintf' => array( Member::loggedIn()->group['g_max_messages'] ), 'pluralize' => array( Member::loggedIn()->msg_count_total ) ) ), 'quotaPercent' => Member::loggedIn()->group['g_max_messages'] ? ( ( 100 / Member::loggedIn()->group['g_max_messages'] ) * Member::loggedIn()->msg_count_total ) : 0, 'totalMessages' => Member::loggedIn()->msg_count_total, 'newMessages' => Member::loggedIn()->msg_count_new, 'counts' => $counts ); } /** * Remove any messenger filters * * @return void */ protected function removeAlertFilter() : void { Session::i()->csrfCheck(); Alert::clearMessengerFilters(); Output::i()->redirect( Url::internal('app=core&module=messaging&controller=messenger&overview=1') ); } }