Файл: IPBMafia.ru_IPB_3.4.6_Final_Rus _Nulled/board/upload/admin/applications/members/sources/classes/messaging/messengerFunctions.php
Строк: 2401
<?php
/**
* <pre>
* Invision Power Services
* IP.Board v3.4.6
* Comments library
* Last Updated: $Date: 2013-10-15 10:55:14 -0400 (Tue, 15 Oct 2013) $
* </pre>
*
* @author $Author: rashbrook $
* @copyright (c) 2001 - 2009 Invision Power Services, Inc.
* @license http://www.invisionpower.com/company/standards.php#license
* @package IP.Board
* @subpackage Members
* @link http://www.invisionpower.com
* @since 20th February 2002
* @version $Revision: 12379 $
*/
if ( ! defined( 'IN_IPB' ) )
{
print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
exit();
}
class messengerFunctions
{
/**
* Registry Object Shortcuts
*
* @var object
*/
protected $registry;
protected $DB;
protected $settings;
protected $request;
protected $lang;
protected $member;
protected $memberData;
protected $cache;
protected $caches;
/**
* Directory Data
*
* @var array
*/
public $_dirData = array();
/**
* Folder Jump HTML
*
* @var string
*/
public $_jumpMenu;
/**
* Current folder ID
*
* @var string
*/
public $_currentFolderID;
/**
* Boolean flag
*
* @var boolean
*/
public $forceMessageToSend = FALSE;
/**
* More exception data
* Should write a custom exception handler really
*
* @var array
*/
public $exceptionData = array();
/**
* No. messages per page to view
*
* @var int
*/
public $messagesPerPage = 20;
/**
* Folder filter.
* Mostly used for 'myconvo' folder currently, but added
* here so it's done proper-like
*
* @var string
*/
protected $_folderFilter = '';
protected $Parser;
/**
* Constructor
*
* @param object $registry Registry object
* @return @e void
*/
public function __construct( ipsRegistry $registry )
{
/* Make object */
$this->registry = $registry;
$this->DB = $this->registry->DB();
$this->settings =& $this->registry->fetchSettings();
$this->request =& $this->registry->fetchRequest();
$this->lang = $this->registry->getClass('class_localization');
$this->member = $this->registry->member();
$this->memberData =& $this->registry->member()->fetchMemberData();
$this->cache = $this->registry->cache();
$this->caches =& $this->registry->cache()->fetchCaches();
//-----------------------------------------
// Need to reset?
//-----------------------------------------
if ( $this->memberData['msg_count_reset'] )
{
$this->memberData['pconversation_filters'] = $this->resetMembersFolderCounts( $this->memberData['member_id'] );
$this->resetMembersTotalTopicCount( $this->memberData['member_id'] );
$this->resetMembersNewTopicCount( $this->memberData['member_id'] );
}
/* Load parser */
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' );
$this->Parser = new $classToLoad();
/* Set up parser */
$this->Parser->set( array( 'memberData' => $this->memberData,
'parseBBCode' => 1,
'parseArea' => 'pms',
'parseHtml' => 0,
'parseEmoticons' => 1 ) );
//-----------------------------------------
// INIT Folder contents
//-----------------------------------------
$folderContents = array();
//-----------------------------------------
// Do a little set up, do a litle dance, get
// down tonight! *boogie*
//-----------------------------------------
$this->_dirData = $this->explodeFolderData( $this->memberData['pconversation_filters'] );
//-----------------------------------------
// Do we have VID?
// No, it's just the way we walk! Haha, etc.
//-----------------------------------------
if ( $this->request['folderID'] AND $this->request['folderID'] )
{
$this->_currentFolderID = $this->request['folderID'];
}
else
{
/* Got any new messages? If so, show that. If not, show myconvo
I'm sure you could have figured that out without this silly comment...*/
$this->_currentFolderID = ( $this->_dirData['new']['count'] ) ? 'new' : 'myconvo';
}
//-----------------------------------------
// Print folder links
//-----------------------------------------
foreach( $this->_dirData as $id => $data )
{
if ( $data['protected'] AND $id != 'myconvo' )
{
continue;
}
$folderContents[] = "<option value='move_$id'>{$data['real']}</option>";
}
if ( count( $folderContents ) > 1 )
{
$this->_jumpMenu = implode( "n", $folderContents );
}
else
{
$this->_jumpMenu = '';
}
}
/**
* Flood control check
*
* @return bool (return TRUE for OK to continue, FALSE for flood stopped) Also populates $this->exceptionData[0] with time that next PM can be made
*/
public function floodControlCheck()
{
/* Disabled PM flood? */
if ( ! $this->memberData['g_pm_flood_mins'] )
{
return TRUE;
}
/* Forcing a PM, bypass the check */
if ( $this->forceMessageToSend === TRUE )
{
return TRUE;
}
/* Ensure we have a member */
if ( ! $this->memberData['member_id'] )
{
return FALSE;
}
/* Still here? Grab their last sent PM */
$pm = $this->DB->buildAndFetch( array( 'select' => 'MAX(mt_date) as max',
'from' => 'message_topics',
'where' => 'mt_starter_id=' . $this->memberData['member_id'] ) );
if ( $pm['max'] )
{
$_check = time() - ( intval( $this->memberData['g_pm_flood_mins'] ) * 60 );
if ( $pm['max'] >= $_check )
{
/* Last PM is more recent */
$this->exceptionData = array( 0 => $this->registry->class_localization->getDate( $pm['max'] + ( intval( $this->memberData['g_pm_flood_mins'] ) * 60 ), 'LONG', 1 ) );
return FALSE;
}
else
{
/* Last PM is older */
return TRUE;
}
}
return TRUE;
}
/**
* Check number of messages sent today vs your max per day setting
*
* @return bool Can send more
*/
public function checkHasHitMax()
{
if( $this->memberData['g_pm_perday'] )
{
$_time = time() - ( 60 * 60 * 24 );
$_total = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as total', 'from' => 'message_topics', 'where' => 'mt_start_time > ' . $_time . ' AND mt_starter_id=' . $this->memberData['member_id'] ) );
if( $_total['total'] >= $this->memberData['g_pm_perday'] )
{
return true;
}
}
return false;
}
/**
* Set a folder filter
* Mostly used for myconvo but added here for extensibility in the future
*
* @param string
* @return @e void
*/
public function addFolderFilter( $filter )
{
$this->_folderFilter = $filter;
}
/**
* Fetch new PM notification
*
* @param int Member ID
* @return array Array of data
*/
public function fetchUnreadNotifications( $memberID )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$return = array();
$memberID = intval( $memberID );
$members = array();
$_members = array();
//-----------------------------------------
// Fetch messages
//-----------------------------------------
$this->DB->build( array( 'select' => 'map.*',
'from' => array( 'message_topic_user_map' => 'map' ),
'where' => 'map.map_user_active=1 AND map.map_user_banned=0 AND map.map_has_unread=1 AND map.map_user_id=' . $memberID,
'add_join' => array( array( 'select' => 't.*',
'from' => array( 'message_topics' => 't' ),
'where' => 't.mt_id=map_topic_id',
'type' => 'inner' ),
array( 'select' => 'p.*',
'from' => array( 'message_posts' => 'p' ),
'where' => 'p.msg_id=t.mt_last_msg_id',
'type' => 'inner' ) ) ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$return[ $row['msg_date'] . '.' . $row['msg_id'] ] = $row;
/* Members to load */
$_members[ $row['mt_starter_id'] ] = $row['mt_starter_id'];
$_members[ $row['msg_author_id'] ] = $row['msg_author_id'];
}
/* Got anything? */
if ( ! count( $return ) )
{
return array();
}
/* Load 'em */
if ( count( $_members ) )
{
$members = IPSMember::load( $_members, 'all' );
}
/* Parse 'em */
if ( count( $members ) )
{
foreach( $members as $id => $data )
{
$members[ $id ] = IPSMember::buildDisplayData( $data );
}
}
/* Add 'em */
foreach( $return as $sortID => $data )
{
/* membahs */
$data['starterData'] = $members[ $data['mt_starter_id'] ];
$data['authorData'] = $members[ $data['msg_author_id'] ];
/* Format message */
$data['msg_post'] = $this->Parser->stripAllTags( $data['msg_post'] ); //$this->_formatMessageForDisplay( $data['msg_post'] );
/* Type of PM */
if ( $data['msg_id'] == $data['mt_first_msg_id'] )
{
$data['_type'] = 'new';
}
else
{
$data['_type'] = 'reply';
}
$return[ $sortID ] = $data;
}
/* Reverse sort it (latest first) */
krsort( $return );
/* Return 'em */
return $return;
}
/**
* Search messages
* Searches messages. It really does.
*
* @param int Member ID who be searching!
* @param string Words to search (probably tainted at this point, so be careful!)
* @param int Offset start
* @param int Number of results to return
* @param array Array of folders to search (send nothing to search all)
* @return array array( 'totalMatches' => int, 'results' => Array of topics that match the search words )
*/
public function searchMessages( $memberID, $words, $start=0, $end=50, $folders=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$results = array( 'totalMatches' => 0, 'results' => array() );
$_memberIDs = array();
$_members = array();
$_results = array();
//-----------------------------------------
// Load up library
//-----------------------------------------
$file = IPSLib::getAppDir( 'members' ) . '/sql/messengerSearch_' . strtolower( $this->settings['sql_driver'] ) . '.php';
if ( ! is_file( $file ) )
{
return $results;
}
//-----------------------------------------
// Fetch 'em
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( $file, 'messengerSearch', 'members' );
$search = new $classToLoad( $this->registry );
$search->execute( $memberID, $words, $start, $end, $folders );
$results['totalMatches'] = $search->fetchTotalRows();
$_results = $search->fetchResults();
//-----------------------------------------
// Add in member data...
//-----------------------------------------
if ( ! count( $_results ) )
{
return $results;
}
foreach( $_results as $mtID => $mtData )
{
$_memberIDs[ $mtData['mt_starter_id'] ] = $mtData['mt_starter_id'];
$_memberIDs[ $mtData['mt_to_member_id'] ] = $mtData['mt_to_member_id'];
}
/* Load members */
if ( count( $_memberIDs ) )
{
$_members = IPSMember::load( array_keys( $_memberIDs ), 'all' );
}
/* Let's sort out the ignore & photos at once for everyone */
if( count($_members) )
{
foreach( $_members as $_mid => $_mdata )
{
$_mdata['_canBeBlocked'] = IPSMember::isIgnorable( $_mdata['member_group_id'], $_mdata['mgroup_others'], 'pm' );
$_members[ $_mid ] = IPSMember::buildProfilePhoto( $_mdata );
}
}
/* Final result parse */
foreach( $_results as $mtID => $mtData )
{
$mtData['_starterMemberData'] = $_members[ $mtData['mt_starter_id'] ];
$mtData['_toMemberData'] = $_members[ $mtData['mt_to_member_id'] ];
$mtData['_lastMsgAuthor'] = $_members[ $mtData['msg_author_id'] ];
$mtData['_folderName'] = $this->_dirData[ $mtData['map_folder_id'] ]['real'];
$results['results'][ $mtID ] = $mtData;
}
return $results;
}
/**
* Block / unblock a member
*
* @param int Member ID to block
* @param int Member ID who is attempting to block
* @param int Topic ID to apply block to
* @param boolean TRUE is block, FALSE is unblock
* @return @e void
*
* <code>
* Exception Codes:
* NO_PERMISSION User does not have permission to access this topic
* NO_BLOCK_PERMISSION User does not have the ability to block anyone
* CANNOT_BLOCK_STARTER Cannot (un)block the topic starter, that's just plain mean!
* CANNOT_BLOCK_USER Cannot (un)block the member because they either are not involved with that topic or they cannot be blocked
* </code>
*/
public function toggleTopicBlock( $blockID, $blockerID, $topicID, $block )
{
$topicData = $this->fetchTopicData( $topicID );
$_members = IPSMember::load( array( $blockID, $blockerID ), 'extendedProfile,groups' );
$blockMemberData = $_members[ $blockID ];
$blockerMemberData = $_members[ $blockerID ];
$currentParticipants = $this->fetchTopicParticipants( $topicID );
/* Can access this topic? */
if ( $this->canAccessTopic( $blockerMemberData['member_id'], $topicData, $currentParticipants, TRUE ) !== TRUE )
{
throw new Exception( 'NO_PERMISSION' );
}
/* Topic starter? */
if ( $topicData['mt_starter_id'] == $blockID )
{
throw new Exception( 'CANNOT_BLOCK_STARTER' );
}
/* Does the person we are attempting to block exist? */
if ( ! isset($currentParticipants[ $blockID ]) )
{
throw new Exception( 'CANNOT_BLOCK_USER' );
}
/* Can they be blocked? */
if ( IPSMember::isIgnorable( $blockMemberData['member_group_id'], $blockMemberData['mgroup_others'], 'pm' ) !== TRUE )
{
throw new Exception( 'CANNOT_BLOCK_USER' );
}
/* Can we actually block anyone? */
if ( ( $topicData['mt_starter_id'] != $blockerID ) AND ( ! $blockerMemberData['g_is_supmod'] ) )
{
throw new Exception( 'NO_BLOCK_PERMISSION' );
}
/* Ok... */
if ( $block === TRUE )
{
$this->DB->update( 'message_topic_user_map', array( 'map_user_active' => 0, 'map_user_banned' => 1 ), 'map_user_id=' . $blockID . ' AND map_topic_id=' . $topicID . ' AND map_user_active=1' );
}
else
{
$this->DB->update( 'message_topic_user_map', array( 'map_user_active' => 1, 'map_user_banned' => 0 ), 'map_user_id=' . $blockID . ' AND map_topic_id=' . $topicID . ' AND map_user_banned=1' );
$this->DB->update( 'message_topics', array( 'mt_to_count' => count( $currentParticipants ) - 1 ), 'mt_id=' . $topicID );
}
$this->resetMembersNewTopicCount( $blockID );
$this->resetMembersTotalTopicCount( $blockID );
$this->resetMembersFolderCounts( $blockID );
}
/**
* Toggles notifications
*
* @param int Owner Member ID
* @param array Array of IDs to toggle read / unread
* @param boolean TRUE notify on / FALSE notify off
* @return bool
*
* <code>
* Exception Codes:
* NO_SUCH_MEMBER: The member ID does not exist
* NO_IDS_SELECTED No IDs to move (empty id array)
* </code>
*/
public function toggleNotificationStatus( $memberID, $ids, $notifyStatus='toggle' )
{
//-----------------------------------------
// Grab data
//-----------------------------------------
$memberData = IPSMember::load( $memberID, 'groups,extendedProfile' );
if ( ! $memberData['member_id'] )
{
throw new Exception( "NO_SUCH_MEMBER");
}
//-----------------------------------------
// Check...
//-----------------------------------------
if ( ! is_array( $ids ) OR ! count( $ids ) )
{
throw new Exception("NO_IDS_SELECTED");
}
$idString = implode( ",", $ids );
//-----------------------------------------
// Toggle...
//-----------------------------------------
if ( $notifyStatus === TRUE )
{
$this->DB->update( 'message_topic_user_map', array( 'map_ignore_notification' => 0 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" );
}
else if ( $notifyStatus == 'toggle' )
{
$_on = array();
$_off = array();
$this->DB->build( array( 'select' => '*',
'from' => 'message_topic_user_map',
'where' => "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
if ( $row['map_ignore_notification'] )
{
$_on[] = $row['map_topic_id'];
}
else
{
$_off[] = $row['map_topic_id'];
}
}
if ( count( $_on ) )
{
$this->DB->update( 'message_topic_user_map', array( 'map_ignore_notification' => 0 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN (" . implode(",",$_on). ")" );
}
if ( count( $_off ) )
{
$this->DB->update( 'message_topic_user_map', array( 'map_ignore_notification' => 1 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN (" . implode(",",$_off). ")" );
}
}
else
{
$this->DB->update( 'message_topic_user_map', array( 'map_ignore_notification' => 1 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" );
}
/* Recount */
$this->resetMembersFolderCounts( $memberData['member_id'] );
return TRUE;
}
/**
* Toggles read/unread status
*
* @param int Owner Member ID
* @param array Array of IDs to toggle read / unread
* @param boolean TRUE mark as read / FALSE mark as unread
* @return bool
*
* <code>
* Exception Codes:
* NO_SUCH_MEMBER: The member ID does not exist
* NO_IDS_SELECTED No IDs to move (empty id array)
* </code>
*/
public function toggleReadStatus( $memberID, $ids, $markAsRead )
{
//-----------------------------------------
// Grab data
//-----------------------------------------
$memberData = IPSMember::load( $memberID, 'groups,extendedProfile' );
if ( ! $memberData['member_id'] )
{
throw new Exception( "NO_SUCH_MEMBER");
}
//-----------------------------------------
// Check...
//-----------------------------------------
if ( ! is_array( $ids ) OR ! count( $ids ) )
{
throw new Exception("NO_IDS_SELECTED");
}
$idString = implode( ",", $ids );
//-----------------------------------------
// Mark as read...
//-----------------------------------------
if ( $markAsRead === TRUE )
{
$this->DB->update( 'message_topic_user_map', array( 'map_has_unread' => 0 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" );
}
else
{
$this->DB->update( 'message_topic_user_map', array( 'map_has_unread' => 1 ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" );
}
/* Recount */
$this->resetMembersFolderCounts( $memberData['member_id'] );
$this->resetMembersNewTopicCount( $memberData['member_id'] );
return TRUE;
}
/**
* Add participants to a topic
*
* @param int Topic ID
* @param array Array of IDs to invite
* @param int Member adding them (ie you)
* @return mixed bool or exception
*
* <code>
* EXCEPTION CODES:
* NO_PERMISSION User does not have permission to access this topic
* NOT_ALL_INVITE_USERS_EXIST Not all the users you've invited exist
* INVITE_USERS_BLOCKED Some of the users you've invited are blocked
* NO_ONE_TO_INVITE No one left to invite
* CANT_INVITE_SELF: The sender is in the invite list
* CANT_INVITE_RECIPIENT: The main recipient is in the invite list
* CANT_INVITE_RECIPIENT_EXIST: One or more recipients are already participating in this conversation
* </code>
*/
public function addTopicParticipants( $topicID, $invitedUsers, $readingMemberID )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$topicData = $this->fetchTopicData( $topicID );
$memberData = IPSMember::load( intval( $readingMemberID ), 'extendedProfile,groups' );
$currentParticipants = $this->fetchTopicParticipants( $topicID );
//-----------------------------------------
// Can access this topic?
//-----------------------------------------
if ( $this->canAccessTopic( $memberData['member_id'], $topicData, $currentParticipants ) !== TRUE )
{
throw new Exception( 'NO_PERMISSION' );
}
//-----------------------------------------
// Build invited users
//-----------------------------------------
$invitedUsersData = $this->checkAndReturnInvitedUsers( $invitedUsers, $memberData, count($currentParticipants) );
if ( isset($invitedUsersData[ $topicData['mt_starter_id'] ]) )
{
if ( $memberData['member_id'] == $topicData['mt_starter_id'] )
{
throw new Exception( 'CANT_INVITE_SELF' );
}
}
if ( isset($invitedUsersData[ $topicData['mt_to_member_id'] ]) OR isset($invitedUsersData[ $topicData['mt_starter_id'] ]) )
{
throw new Exception( 'CANT_INVITE_RECIPIENT_EXIST' );
}
//-----------------------------------------
// Knock out ones that are already participated
// At this point $currentParticipants contains non-active
// participants. This is OK as we don't want the ability
// to keep re-inviting those who choose to leave.
//-----------------------------------------
$existInBoth = array_intersect( array_keys( $currentParticipants ), array_keys( $invitedUsersData ) );
if ( count( $existInBoth ) )
{
foreach( $existInBoth as $id )
{
unset( $invitedUsersData[ $id ] );
}
}
//-----------------------------------------
// Anything left?
//-----------------------------------------
if ( ! count( $invitedUsersData ) )
{
throw new Exception( 'NO_ONE_TO_INVITE' );
}
//-----------------------------------------
// Update the topic
// Now we can strip non-active participants
//-----------------------------------------
$__topicParticipants = array_merge( array_keys( $this->_stripNonActiveParticipants( $currentParticipants ) ), array_keys( $invitedUsersData ) );
/* Fix up so they're unique and indexed by member ID */
if ( is_array( $__topicParticipants ) AND count ( $__topicParticipants ) )
{
foreach( $__topicParticipants as $_mid )
{
$_topicParticipants[ $_mid ] = $_mid;
}
}
/* Remove topic starter */
unset( $_topicParticipants[ $topicData['mt_starter_id'] ] );
/* Remove recipient */
unset( $_topicParticipants[ $topicData['mt_to_member_id'] ] );
$this->DB->update( 'message_topics', array( 'mt_invited_members' => serialize( $_topicParticipants ),
'mt_to_count' => count( $_topicParticipants ) + 1 ), 'mt_id=' . $topicID );
//-----------------------------------------
// Add the users to the invite tree
//-----------------------------------------
foreach( $invitedUsersData as $id => $toMember )
{
$this->DB->insert( 'message_topic_user_map', array( 'map_user_id' => $id,
'map_topic_id' => $topicID,
'map_folder_id' => 'myconvo',
'map_user_active' => 1,
'map_has_unread' => 1,
'map_user_banned' => 0,
'map_read_time' => 0,
'map_left_time' => 0,
'map_ignore_notification' => 0,
'map_last_topic_reply' => $topicData['mt_last_post_time'] ) );
$new_vdir = $this->rebuildFolderCount( $toMember['member_id'], array( 'myconvo' => 'plus:1', 'new' => 'plus:1' ), TRUE, array( 'core' => array( 'msg_count_total' => 'plus:1',
'msg_count_new' => 'plus:1' ) ) );
//-----------------------------------------
// Notifications library
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
$notifyLibrary = new $classToLoad( $this->registry );
$toMember['language'] = $toMember['language'] == "" ? IPSLib::getDefaultLanguage() : $toMember['language'];
IPSText::getTextClass('email')->getTemplate( "personal_convo_invite", $toMember['language'] );
IPSText::getTextClass('email')->buildMessage( array(
'NAME' => $toMember['members_display_name'],
'POSTER' => $memberData['members_display_name'],
'TITLE' => $topicData['mt_title'],
'LINK' => "?app=members&module=messaging§ion=view&do=showConversation&topicID={$topicID}" ) );
IPSText::getTextClass('email')->subject = sprintf(
IPSText::getTextClass('email')->subject,
$this->registry->output->buildSEOUrl( 'showuser=' . $memberData['member_id'], 'public', $memberData['members_seo_name'], 'showuser' ),
$memberData['members_display_name'],
$this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=showConversation&topicID={$topicID}", 'public' )
);
$notifyLibrary->setMember( $toMember );
$notifyLibrary->setFrom( $memberData );
$notifyLibrary->setNotificationKey( 'invite_private_message' );
$notifyLibrary->setNotificationUrl( $this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=showConversation&topicID={$topicID}", 'public' ) );
$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message );
$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
try
{
$notifyLibrary->sendNotification();
}
catch( Exception $e ){}
}
return TRUE;
}
/**
* Delete personal topics
*
* @param int Owner Member ID
* @param array Array of IDs to delete
* @param string [ Raw SQL to query on when selecting messages to delete, optional ]
* @param bool Force hard delete
* @return mixed bool or exception
*
* <code>
* Exception Codes:
* NO_SUCH_MEMBER: The member ID does not exist
* NO_IDS_TO_DELETE: No IDs to delete (empty id array)
* </code>
*/
public function deleteTopics( $memberID, $ids, $rawSQL=NULL, $hardDelete=false )
{
//-----------------------------------------
// Grab data
//-----------------------------------------
$memberData = IPSMember::load( $memberID, 'groups,extendedProfile' );
$final_ids = array();
$final_mts = array();
$starter = array();
$wanttoleave = array();
$participater = array();
$system = array();
$deleted_ids = array();
$attach_ids = array();
$toHardDelete = array();
$membahs = array( $memberID => $memberID );
if ( ! $memberData['member_id'] )
{
throw new Exception( "NO_SUCH_MEMBER");
}
//-----------------------------------------
// Check...
//-----------------------------------------
if ( ! is_array( $ids ) OR ! count( $ids ) )
{
throw new Exception("NO_IDS_TO_DELETE");
}
//-----------------------------------------
// Finish raw SQL
//-----------------------------------------
if ( $rawSQL !== NULL )
{
$rawSQL = ' AND ' . $rawSQL;
}
$idString = implode( ",", IPSLib::cleanIntArray( $ids ) );
//-----------------------------------------
// Grab all affected members...
//-----------------------------------------
$this->DB->build( array( 'select' => '*',
'from' => 'message_topic_user_map',
'where' => 'map_topic_id IN(' . $idString . ')' ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$membahs[ $row['map_user_id'] ] = $row['map_user_id'];
}
/* Flag for recount */
$this->_flagForCountReset( $membahs );
//-----------------------------------------
// Get messages?
//-----------------------------------------
$this->DB->build( array( 'select' => 'mt.*',
'from' => array( 'message_topics' => 'mt' ),
'where' => "mt.mt_id IN(" . $idString . ")" . $rawSQL,
'add_join' => array( array( 'select' => 'map.*',
'from' => array( 'message_topic_user_map' => 'map' ),
'where' => 'map.map_topic_id=mt.mt_id AND map.map_user_id=' . $memberData['member_id'],
'type' => 'left' ) ) ) );
$this->DB->execute();
/* Build up topics to remove */
while ( $i = $this->DB->fetch() )
{
/* Starter? */
if ( $i['mt_starter_id'] == $memberData['member_id'] )
{
$starter[ $i['mt_id'] ] = $i;
$allTopics[ $i['mt_id'] ] = $i;
}
else if ( $i['map_user_id'] AND $i['mt_is_system'] )
{
$system[ $i['mt_id'] ] = $i;
$allTopics[ $i['mt_id'] ] = $i;
}
else if ( $i['map_user_id'] )
{
$wanttoleave[ $i['mt_id'] ] = $i;
$allTopics[ $i['mt_id'] ] = $i;
}
}
//-----------------------------------------
// Are we forcing a hard delete
//-----------------------------------------
if( $hardDelete )
{
$idsForAttachments = array();
$this->DB->build( array( 'select' => 'msg_id',
'from' => 'message_posts',
'where' => 'msg_topic_id IN ('. $idString . ')'
) );
$this->DB->execute();
while( $r = $this->DB->fetch() )
{
$idsForAttachments[] = $r['msg_id'];
}
/* Delete Topics */
$this->DB->delete( 'message_topics', 'mt_id IN ('. $idString . ')' . $rawSQL );
/* Delete Posts */
$this->DB->delete( 'message_posts', 'msg_topic_id IN ('. $idString . ')' . $rawSQL );
/* Delete mappings */
$this->DB->delete( 'message_topic_user_map', 'map_topic_id IN ('. $idString . ')' . $rawSQL );
//-----------------------------------------
// Delete attachments
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'core' ) . '/sources/classes/attach/class_attach.php', 'class_attach' );
$class_attach = new $classToLoad( $this->registry );
$class_attach->type = 'msg';
$class_attach->init();
$class_attach->bulkRemoveAttachment( $idsForAttachments );
}
else
{
//-----------------------------------------
// System PMs, delete everything to do with them
//-----------------------------------------
if ( count( $system ) )
{
/* Delete Topics */
$this->DB->delete( 'message_topics', 'mt_id IN ('. implode( ',', array_keys( $system ) ) . ')' );
/* Delete Posts */
$this->DB->delete( 'message_posts', 'msg_topic_id IN ('. implode( ',', array_keys( $system ) ) . ')' );
/* Delete mappings */
$this->DB->delete( 'message_topic_user_map', 'map_topic_id IN ('. implode( ',', array_keys( $system ) ) . ')' );
}
//-----------------------------------------
// Ones I started, soft delete
//-----------------------------------------
if ( count( $starter ) )
{
/* First step: Soft delete them */
$this->DB->update( 'message_topics', 'mt_to_count=mt_to_count-1,mt_is_deleted=1', 'mt_id IN (' . implode( ',', array_keys( $starter ) ) . ')', FALSE, TRUE );
/* Update mappings for member */
$this->DB->update( 'message_topic_user_map', array( 'map_user_active' => 0 ), 'map_user_id=' . $memberData['member_id'] . ' AND map_topic_id IN ('. implode( ',', array_keys( $starter ) ) . ')' );
}
//-----------------------------------------
// Ones that I just want to leave..
//-----------------------------------------
if ( count( $wanttoleave ) )
{
/* De-activate participant row */
$this->DB->update( 'message_topic_user_map', array( 'map_user_active' => 0 ), 'map_user_id=' . $memberData['member_id'] . ' AND map_topic_id IN ('. implode( ',', array_keys( $wanttoleave ) ) . ')' );
/* Update counts */
$this->DB->update( 'message_topics', 'mt_to_count=mt_to_count-1', 'mt_id IN (' . implode( ',', array_keys( $wanttoleave ) ) . ')', FALSE, TRUE );
}
//-----------------------------------------
// Right. Now lets find all (partici)pantless topics
//-----------------------------------------
if ( count( $allTopics ) )
{
$this->DB->build( array( 'select' => 'mt.*',
'from' => array( 'message_topics' => 'mt' ),
'where' => "mt.mt_id IN(" . implode( ',', array_keys( $allTopics ) ) . ")",
'add_join' => array( array( 'select' => 'map.*',
'from' => array( 'message_topic_user_map' => 'map' ),
'where' => 'map.map_topic_id=mt.mt_id AND map.map_user_active=1',
'type' => 'left' ) ) ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
/* Not got -any- mapping? */
if ( ! $row['map_user_id'] )
{
$toHardDelete[ $row['mt_id'] ] = $row;
}
}
$idsForAttachments = array();
$this->DB->build( array( 'select' => 'msg_id',
'from' => 'message_posts',
'where' => 'msg_topic_id IN ('. implode( ',', array_keys( $allTopics ) ) . ')'
) );
$this->DB->execute();
while( $r = $this->DB->fetch() )
{
$idsForAttachments[] = $r['msg_id'];
}
}
//-----------------------------------------
// Hard delete MT topics
//-----------------------------------------
if ( count($toHardDelete) )
{
$this->DB->delete( 'message_topics', "mt_id IN (".implode( ',', array_keys( $toHardDelete ) ).")" );
//-----------------------------------------
// Delete attachments
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'core' ) . '/sources/classes/attach/class_attach.php', 'class_attach' );
$class_attach = new $classToLoad( $this->registry );
$class_attach->type = 'msg';
$class_attach->init();
$class_attach->bulkRemoveAttachment( array_keys( $idsForAttachments ) );
//-----------------------------------------
// Delete posts and mapping
//-----------------------------------------
$this->DB->delete( 'message_posts', 'msg_topic_id IN (' . implode( ',', array_keys( $toHardDelete ) ) . ')' );
$this->DB->delete( 'message_topic_user_map', 'map_topic_id IN (' . implode( ',', array_keys( $toHardDelete ) ) . ')' );
}
}
return TRUE;
}
/**
* Moves messages into another folder
*
* @param int Owner Member ID
* @param array Array of IDs to move
* @param string 'id' of folder to move to
* @return mixed bool or exception
*
* <code>
* Exception Codes:
* NO_SUCH_MEMBER: The member ID does not exist
* NO_SUCH_FOLDER: Folder you are attemting to move to does not exist
* NO_IDS_TO_MOVE No IDs to move (empty id array)
* </code>
*/
public function moveTopics( $memberID, $ids, $toFolderID )
{
//-----------------------------------------
// Grab data
//-----------------------------------------
$memberData = IPSMember::load( $memberID, 'groups,extendedProfile' );
if ( ! $memberData['member_id'] )
{
throw new Exception( "NO_SUCH_MEMBER");
}
//-----------------------------------------
// Check...
//-----------------------------------------
if ( ! is_array( $ids ) OR ! count( $ids ) )
{
throw new Exception("NO_IDS_TO_MOVE");
}
$idString = implode( ",", $ids );
//-----------------------------------------
// First off, get dir data
//-----------------------------------------
$folders = $this->explodeFolderData( $memberData['pconversation_filters'] );
//-----------------------------------------
// Got a folder with that ID?
//-----------------------------------------
if ( ! $folders[ $toFolderID ] )
{
throw new Exception("NO_SUCH_FOLDER");
}
//-----------------------------------------
// Move the messages...
//-----------------------------------------
$this->DB->update( 'message_topic_user_map', array( 'map_folder_id' => $toFolderID ), "map_user_id=".$memberData['member_id']." AND map_topic_id IN ($idString)" );
/* Recount */
$this->resetMembersFolderCounts( $memberData['member_id'] );
return TRUE;
}
/**
* Edits a message
*
* @param int FROM Member ID
* @param int Topic ID
* @param int Msg ID
* @param string Message Content
* @return mixed TRUE or FALSE or Exception
*
* <code>
* Exception Codes:
* MSG_CONTENT_EMPTY: The 'msgContent' varable is empty
* NO_SUCH_TOPIC: No such topic was found
* NO_PERMISSION: No permission to read/reply
* NO_EDIT_PERMISSION: No permission to edit
* TOPIC_HAS_BEEN_DELETED: Topic has been "deleted" by the topic starter
* </code>
*/
public function sendEdit( $fromMemberID, $topicID, $msgID, $msgContent, $options=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$topicData = array();
$messageData = array();
$fromMemberData = array();
$remapData = array();
//-----------------------------------------
// Check content
//-----------------------------------------
if ( ! $msgContent )
{
throw new Exception( 'MSG_CONTENT_EMPTY' );
}
//-----------------------------------------
// Format content
//-----------------------------------------
try
{
$msgContent = $this->_formatMessageForSaving( $msgContent );
}
catch( Exception $error )
{
throw new Exception( $error->getMessage() );
}
/* Fetch topic data */
$topicData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'message_topics',
'where' => 'mt_id=' . $topicID ) );
if ( ! $topicData['mt_id'] )
{
throw new Exception( 'NO_SUCH_TOPIC' );
}
if ( $topicData['mt_is_deleted'] )
{
throw new Exception( 'TOPIC_HAS_BEEN_DELETED' );
}
/* Fetch remap Data */
$this->DB->build( array( 'select' => '*',
'from' => 'message_topic_user_map',
'where' => 'map_topic_id=' . $topicID ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$remapData[ $row['map_user_id'] ] = $row;
}
/* Got us? */
if ( $this->canAccessTopic( $fromMemberID, $topicData ) !== TRUE )
{
throw new Exception( "NO_PERMISSION" );
}
/* Fetch message data */
$messageData = $this->fetchMessageData( $topicID, $msgID );
/* Reset Post Key */
$options['postKey'] = ( $messageData['msg_post_key'] ) ? $messageData['msg_post_key'] : md5(microtime());
//-----------------------------------------
// Load member data
//-----------------------------------------
$memberData = IPSMember::load( array_keys( $remapData ), 'groups,extendedProfile' );
$fromMemberData = $memberData[ $fromMemberID ];
/* Can edit? */
if ( $topicData['mt_is_deleted'] OR $this->_conversationCanEdit( $messageData, $topicData, $fromMemberData ) !== TRUE )
{
throw new Exception( "NO_EDIT_PERMISSION" );
}
//-----------------------------------------
// Update the post...
//-----------------------------------------
$this->DB->update( 'message_posts', array( 'msg_post' => $msgContent ), 'msg_id=' . $msgID );
/* Fetch attachment count */
$count = intval( $this->_makeAttachmentsPermanent( $options['postKey'], $msgID, $topicID ) );
if ( $count )
{
$this->DB->update( 'message_topics', array( 'mt_hasattach' => $count ), 'mt_id=' . $topicID );
}
return TRUE;
}
/**
* Sends a reply to a PM
*
* @param int FROM Member ID
* @param int Topic ID
* @param string Message Content
* @return mixed Msg ID or exception
*
* <code>
* Exception Codes:
* MSG_CONTENT_EMPTY: The 'msgContent' varable is empty
* NO_SUCH_TOPIC: No such topic was found
* NO_PERMISSION: No permission to read/reply
* TOPIC_HAS_BEEN_DELETED: Topic has been "deleted" by the topic starter
* TOPIC_IS_SYSTEM: This is a system topic and cannot be replied to
* FROM_USER_BLOCKED: Cannot send as starter has blocked fromMemberID
* </code>
*/
public function sendReply( $fromMemberID, $topicID, $msgContent, $options=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$topicData = array();
$fromMemberData = array();
$remapData = array();
$options['postKey'] = ( $options['postKey'] ) ? $options['postKey'] : md5(microtime());
//-----------------------------------------
// Check content
//-----------------------------------------
if ( ! $msgContent )
{
throw new Exception( 'MSG_CONTENT_EMPTY' );
}
//-----------------------------------------
// Format content
//-----------------------------------------
try
{
$msgContent = $this->_formatMessageForSaving( $msgContent );
}
catch( Exception $error )
{
throw new Exception( $error->getMessage() );
}
/* Fetch topic data */
$topicData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'message_topics',
'where' => 'mt_id=' . $topicID ) );
if ( ! $topicData['mt_id'] )
{
throw new Exception( 'NO_SUCH_TOPIC' );
}
if ( $topicData['mt_is_deleted'] )
{
throw new Exception( 'TOPIC_HAS_BEEN_DELETED' );
}
if ( $topicData['mt_is_system'] )
{
throw new Exception( 'TOPIC_IS_SYSTEM' );
}
/* Fetch remap Data */
$this->DB->build( array( 'select' => '*',
'from' => 'message_topic_user_map',
'where' => 'map_user_active=1 AND map_topic_id=' . $topicID ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$remapData[ $row['map_user_id'] ] = $row;
}
/* Got us? */
if ( ! isset($remapData[ $fromMemberID ]) )
{
throw new Exception( "NO_PERMISSION" );
}
//-----------------------------------------
// Load member data
//-----------------------------------------
$memberData = IPSMember::load( array_keys( $remapData ), 'groups,extendedProfile' );
$fromMemberData = $memberData[ $fromMemberID ];
//-----------------------------------------
// Has the 'to' use blocked us?
//-----------------------------------------
if ( count( $this->blockedByUser( $fromMemberData, $topicData['mt_to_member_id'] ) ) )
{
if ( $this->forceMessageToSend !== TRUE )
{
$this->exceptionData = array( 0 => $memberData[ $topicData['mt_to_member_id'] ]['members_display_name'] );
throw new Exception( 'FROM_USER_BLOCKED' );
}
}
//-----------------------------------------
// Add the post...
//-----------------------------------------
$_messagePostData = array(
'msg_date' => time(),
'msg_topic_id' => $topicID,
'msg_post' => $msgContent,
'msg_post_key' => md5( microtime() ),
'msg_author_id' => $fromMemberData['member_id'],
'msg_ip_address' => $this->member->ip_address
);
/* Data Hook Location */
IPSLib::doDataHooks( $_messagePostData, 'messengerSendReplyData' );
$this->DB->insert( 'message_posts', $_messagePostData );
$msg_id = $this->DB->getInsertId();
//-----------------------------------------
// Update topic
//-----------------------------------------
$this->DB->update( 'message_topics', array( 'mt_last_post_time' => IPS_UNIX_TIME_NOW,
'mt_hasattach' => ($topicData['mt_hasattach'] + intval( $this->_makeAttachmentsPermanent( $options['postKey'], $msg_id, $topicID ) )),
'mt_replies' => $topicData['mt_replies'] + 1,
'mt_last_msg_id' => $msg_id ), 'mt_id=' . $topicID );
/* Update mapping */
$this->DB->update( 'message_topic_user_map', array( 'map_has_unread' => 1 ), 'map_topic_id=' . $topicID . ' AND map_user_id != ' . $fromMemberID );
/* Update last reply time */
$this->DB->update( 'message_topic_user_map', array( 'map_last_topic_reply' => IPS_UNIX_TIME_NOW ), 'map_topic_id=' . $topicID );
//-----------------------------------------
// Update receipients....
//-----------------------------------------
foreach ($remapData as $memberID => $_remapData )
{
$toMember = $memberData[ $memberID ];
//-----------------------------------------
// Disabled messenger
//-----------------------------------------
if( $toMember['members_disable_pm'] )
{
continue;
}
//-----------------------------------------
// Let them know there's a new PM reply
//-----------------------------------------
if ( $memberID != $fromMemberID )
{
/**
* @link http://community.invisionpower.com/tracker/issue-37272-pm-notification-counter-not-increasing/page__catfilter__1
*/
/*if ( $_remapData['map_ignore_notification'] )
{
IPSMember::save( $memberID, array( 'core' => array( 'msg_count_reset' => 1 ) ) );
}
else
{
IPSMember::save( $memberID, array( 'core' => array( 'msg_count_new' => intval( $this->getPersonalTopicsCount( $memberID, 'new' ) ),
'msg_count_reset' => 1 ) ) );
}*/
if ( empty($_remapData['map_ignore_notification']) && empty($_remapData['map_has_unread']) )
{
IPSMember::save( $memberID, array( 'core' => array( 'msg_count_new' => 'plus:1', 'msg_count_reset' => 1 ) ) );
}
}
//-----------------------------------------
// Notifications library
//-----------------------------------------
if ( $memberID != $fromMemberID AND ! $_remapData['map_ignore_notification'] )
{
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
$notifyLibrary = new $classToLoad( $this->registry );
$buildMessage = array( 'NAME' => $toMember['members_display_name'],
'POSTER' => $fromMemberData['members_display_name'],
'TITLE' => $topicData['mt_title'],
'TEXT' => $msgContent,
'LINK' => "?app=members&module=messaging§ion=view&do=findMessage&topicID={$topicID}&msgID=__firstUnread__" );
IPSText::getTextClass('email')->setPlainTextTemplate( IPSText::getTextClass('email')->getTemplate( 'personal_convo_new_reply', $toMember['language'] ) );
IPSText::getTextClass('email')->buildPlainTextContent( $buildMessage );
IPSText::getTextClass('email')->subject = sprintf( IPSText::getTextClass('email')->subject,
$this->registry->output->buildSEOUrl( 'showuser=' . $fromMemberData['member_id'], 'public', $fromMemberData['members_seo_name'], 'showuser' ),
$fromMemberData['members_display_name'],
$this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=findMessage&topicID={$topicID}&msgID=__firstUnread__", 'public' )
);
$notifyLibrary->setMember( $toMember );
$notifyLibrary->setFrom( $fromMemberData );
$notifyLibrary->setNotificationKey( 'reply_private_message' );
$notifyLibrary->setNotificationUrl( $this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=findMessage&topicID={$topicID}&msgID=__firstUnread__", 'public' ) );
$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->getPlainTextContent() );
$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
$notifyLibrary->setNotificationHtml( IPSText::getTextClass('email')->buildHtmlContent( $buildMessage ) );
$notifyLibrary->setMetaData( array( 'meta_area' => 'pm', 'meta_id' => $topicID, 'meta_app' => 'members' ) );
try
{
$notifyLibrary->sendNotification();
}
catch( Exception $e ){}
}
}
return $msg_id;
}
/**
* Sends a new personal message. Very simple.
*
* @param int TO Member ID
* @param int FROM Member ID
* @param array Array of InviteUser Names (display name)
* @param string Message Title
* @param string Message Content
* @param array Options array[ 'isSystem' (if true, then user will have no record of sending this PM) postKey, 'isDraft', 'sendMode' (invite/copy), 'topicID' ] If a topicID is passed, it's presumed that it was a draft....
* @return mixed TRUE or FALSE or Exception
*
* <code>
* Exception Codes:
* TOPIC_ID_NOT_EXISTS: Topic ID does not exist (re-sending a draft)
* NOT_ALL_INVITE_USERS_EXIST: Not all invite users exist (check $this->exceptionData for a list of names)
* NOT_ALL_INVITE_USERS_CAN_PM: Not all invite users can PM (check $this->exceptionData for a list of names)
* INVITE_USERS_BLOCKED: Some invite users have been blocked (check $this->exceptionData for a list of names)
* TO_USER_DOES_NOT_EXIST: The 'to' user ID does not exist
* FROM_USER_DOES_NOT_EXIST: The 'from' user ID does not exist
* TO_USER_CANNOT_USE_PM: The 'to' user does not have access to PM system
* TO_USER_FULL: The 'to' user cannot accept any more PMs (inbox full)
* FROM_USER_BLOCKED: The 'from' user has been blocked by the 'to' user
* CANNOT_SAVE_TO_SENT_FOLDER: The 'from' user does not have space to store a copy of the message in their sent folder
* MSG_TITLE_EMPTY: The 'msgTitle' variable is empty
* MSG_CONTENT_EMPTY: The 'msgContent' varable is empty
* CANT_SEND_TO_SELF: The main recipient and sender are the same
* CANT_INVITE_SELF: The sender is in the invite list
* CANT_INVITE_RECIPIENT: The main recipient is in the invite list
* FLOOD_STOP Flood control will not allow this message to send
* </code>
*/
public function sendNewPersonalTopic( $toMemberID, $fromMemberID, $inviteUsers, $msgTitle, $msgContent, $options=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$toMemberData = array();
$fromMemberData = array();
$inviteUsersData = array();
$isDraft = ( $options['isDraft'] ) ? TRUE : FALSE;
$isCopyTo = ( $options['sendMode'] == 'copy' ) ? TRUE : FALSE;
$isSystem = ( $options['isSystem'] === TRUE || $options['isSystem'] == 1 ) ? 1 : 0;
$options['postKey'] = ( $options['postKey'] ) ? $options['postKey'] : md5(microtime());
/* Set up force message*/
$this->forceMessageToSend = ( $this->forceMessageToSend ) ? $this->forceMessageToSend : ( ( $options['forcePm'] ) ? TRUE : FALSE );
//-----------------------------------------
// Check content
//-----------------------------------------
if ( $toMemberID == $fromMemberID )
{
throw new Exception( 'CANT_SEND_TO_SELF' );
}
if ( ! $msgTitle )
{
throw new Exception( 'MSG_TITLE_EMPTY' );
}
if ( ! $msgContent )
{
throw new Exception( 'MSG_CONTENT_EMPTY' );
}
//-----------------------------------------
// Format content
//-----------------------------------------
try
{
$_originalMessageContent = $msgContent;
$msgContent = $this->_formatMessageForSaving( $msgContent );
}
catch( Exception $error )
{
throw new Exception( $error->getMessage() );
}
//-----------------------------------------
// First off, load the to and from members
//-----------------------------------------
$_members = IPSMember::load( array( $toMemberID, $fromMemberID ), 'groups,extendedProfile' );
$toMemberData = $this->_setMaxMessages( $_members[ $toMemberID ] );
$fromMemberData = $this->_setMaxMessages( $_members[ $fromMemberID ] );
if ( empty( $toMemberData['member_id'] ) AND $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'TO_USER_DOES_NOT_EXIST' );
}
if ( empty( $fromMemberData['member_id'] ) AND $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'FROM_USER_DOES_NOT_EXIST' );
}
if ( $this->floodControlCheck() !== TRUE )
{
throw new Exception( 'FLOOD_STOP' );
}
//-----------------------------------------
// Sort out invite users
//-----------------------------------------
if ( is_array( $inviteUsers ) AND count( $inviteUsers ) )
{
try
{
if ( $fromMemberData['g_max_mass_pm'] > 0 OR $this->forceMessageToSend === TRUE )
{
$inviteUsersData = $this->checkAndReturnInvitedUsers( $inviteUsers );
}
}
catch( Exception $error )
{
if ( $this->forceMessageToSend !== TRUE )
{
throw new Exception( $error->getMessage() );
}
}
if ( isset($inviteUsersData[ $fromMemberID ]) )
{
throw new Exception( 'CANT_INVITE_SELF' );
}
if ( isset($inviteUsersData[ $toMemberID ]) )
{
throw new Exception( 'CANT_INVITE_RECIPIENT' );
}
}
//-----------------------------------------
// Can the 'to' user accept a PM?
//-----------------------------------------
if ( $this->canUsePMSystem( $toMemberData ) !== TRUE )
{
if ( $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'TO_USER_CANNOT_USE_PM' );
}
}
//-----------------------------------------
// Does the 'to' user have enough space?
//-----------------------------------------
if ( $this->withinPMQuota( $toMemberData ) !== TRUE )
{
if ( $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'TO_USER_FULL' );
}
}
//-----------------------------------------
// Has the 'to' use blocked us?
//-----------------------------------------
if ( count( $this->blockedByUser( $fromMemberData, $toMemberData ) ) )
{
if ( $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'FROM_USER_BLOCKED' );
}
}
//-----------------------------------------
// Is this simply a copy-to?
//-----------------------------------------
if ( $isCopyTo === TRUE AND !$isDraft )
{
/* Send out the main one */
$this->sendNewPersonalTopic( $toMemberID, $fromMemberID, array(), $msgTitle, $_originalMessageContent, array( 'postKey' => $options['postKey'] ) );
/* Send out copy-tos */
foreach ($inviteUsersData as $id => $toMember)
{
$_newPostKey = md5( microtime() );
$_newContent = $_originalMessageContent;
/* We need to duplicate the attachment record for each copy sent */
$this->DB->build( array( 'select' => '*', 'from' => 'attachments', 'where' => "attach_post_key='{$options['postKey']}'" ) );
$outer = $this->DB->execute();
$idRemap = array();
while( $attachData = $this->DB->fetch($outer) )
{
$attachData['attach_post_key'] = $_newPostKey;
$_oldAttachId = $attachData['attach_id'];
unset( $attachData['attach_id'] );
$this->DB->insert( 'attachments', $attachData );
$idRemap[ $_oldAttachId ] = $this->DB->getInsertId();
}
if( count($idRemap) )
{
foreach( $idRemap as $_oldId => $_newId )
{
$_newContent = str_replace( '=' . $_oldId . ':', '=' . $_newId . ':', $_newContent );
}
}
$this->sendNewPersonalTopic( $toMember['member_id'], $fromMemberID, array(), $msgTitle, $_newContent, array( 'postKey' => $_newPostKey ) );
}
/* Done */
return TRUE;
}
//-----------------------------------------
// Insert the user data
//-----------------------------------------
$_count = count( $inviteUsersData );
//-----------------------------------------
// Got a topic ID?
//-----------------------------------------
if ( $options['topicID'] )
{
/* Fetch topic data */
$_topicData = $this->fetchTopicData( $options['topicID'] );
if ( ! $_topicData['mt_id'] AND $this->forceMessageToSend !== TRUE )
{
throw new Exception( 'TOPIC_ID_NOT_EXISTS' );
}
$this->DB->setDataType( 'mt_title', 'string' );
/* First off, update message_topics and message_posts... */
$this->DB->update( 'message_topics', array( 'mt_date' => time(),
'mt_title' => $msgTitle,
'mt_starter_id' => $fromMemberData['member_id'],
'mt_start_time' => time(),
'mt_last_post_time' => time(),
'mt_invited_members' => serialize( array_keys( $inviteUsersData ) ),
'mt_to_count' => count( array_keys( $inviteUsersData ) ) + 1,
'mt_to_member_id' => $toMemberData['member_id'],
'mt_is_draft' => intval( $isDraft ) ), 'mt_id=' . $_topicData['mt_id'] );
/* Now the posts ... */
$this->DB->update( 'message_posts', array( 'msg_date' => time(),
'msg_topic_id' => $_topicData['mt_id'],
'msg_post' => $msgContent,
'msg_author_id' => $fromMemberData['member_id'],
'msg_is_first_post' => 1,
'msg_ip_address' => $this->member->ip_address ), 'msg_id=' . $_topicData['mt_first_msg_id'] );
/* Delete any current user mapping as this will be sorted out below */
$this->DB->delete( 'message_topic_user_map', 'map_topic_id=' . $_topicData['mt_id'] );
/* Reset variable IDs */
$msg_topic_id = $_topicData['mt_id'];
$msg_id = $_topicData['mt_first_msg_id'];
}
//-----------------------------------------
// Insert new...
//-----------------------------------------
else
{
$_messageTopicData = array(
'mt_date' => time(),
'mt_title' => $msgTitle,
'mt_starter_id' => $fromMemberData['member_id'],
'mt_start_time' => IPS_UNIX_TIME_NOW,
'mt_last_post_time' => IPS_UNIX_TIME_NOW,
'mt_invited_members' => serialize( array_keys( $inviteUsersData ) ),
'mt_to_count' => count( array_keys( $inviteUsersData ) ) + 1,
'mt_to_member_id' => $toMemberData['member_id'],
'mt_is_draft' => ( $isDraft ) ? 1 : 0,
'mt_is_system' => $isSystem,
'mt_replies' => 0
);
/* Create topic entry */
$this->DB->setDataType( 'mt_title', 'string' );
/* Data Hook Location */
IPSLib::doDataHooks( $_messageTopicData, 'messengerSendTopicData' );
$this->DB->insert( 'message_topics', $_messageTopicData );
$msg_topic_id = $this->DB->getInsertId();
$_messagePostData = array(
'msg_date' => time(),
'msg_topic_id' => $msg_topic_id,
'msg_post' => $msgContent,
'msg_post_key' => $options['postKey'],
'msg_author_id' => $fromMemberData['member_id'],
'msg_is_first_post' => 1,
'msg_ip_address' => $this->member->ip_address
);
/* Data Hook Location */
IPSLib::doDataHooks( $_messagePostData, 'messengerSendTopicFirstPostData' );
$this->DB->insert( 'message_posts', $_messagePostData );
$msg_id = $this->DB->getInsertId();
}
//-----------------------------------------
// Update with last / first msg ID
//-----------------------------------------
$this->DB->update( 'message_topics', array( 'mt_last_msg_id' => $msg_id,
'mt_first_msg_id' => $msg_id,
'mt_hasattach' => intval( $this->_makeAttachmentsPermanent( $options['postKey'], $msg_id, $msg_topic_id ) ) ), 'mt_id=' . $msg_topic_id );
//-----------------------------------------
// Not a draft?
//-----------------------------------------
if ( $isDraft !== TRUE )
{
//-----------------------------------------
// Add in 'to user' and 'from user' to the cc array
//-----------------------------------------
$inviteUsersData[ $toMemberData['member_id'] ] = $toMemberData;
$inviteUsersData[ $fromMemberData['member_id'] ] = $fromMemberData;
//-----------------------------------------
// Loop....
//-----------------------------------------
foreach( $inviteUsersData as $id => $toMember )
{
//-----------------------------------------
// Enter the info into the DB
// Target user side.
//-----------------------------------------
$_isStarter = ( $fromMemberData['member_id'] == $toMember['member_id'] ) ? 1 : 0;
$_isSystem = ( $fromMemberData['member_id'] == $toMember['member_id'] AND $isSystem ) ? 1 : 0;
$_isActive = ( $_isSystem ) ? 0 : 1;
/* Create user map entry */
$this->DB->insert( 'message_topic_user_map', array( 'map_user_id' => $toMember['member_id'],
'map_topic_id' => $msg_topic_id,
'map_folder_id' => 'myconvo',
'map_user_active' => $_isActive,
'map_is_starter' => ( $fromMemberData['member_id'] == $toMember['member_id'] ) ? 1 : 0,
'map_has_unread' => ( $fromMemberData['member_id'] == $toMember['member_id'] ) ? 0 : 1,
'map_read_time' => ( $fromMemberData['member_id'] == $toMember['member_id'] ) ? IPS_UNIX_TIME_NOW : 0,
'map_is_system' => $_isSystem,
'map_user_banned' => 0,
'map_left_time' => 0,
'map_ignore_notification' => 0,
'map_last_topic_reply' => IPS_UNIX_TIME_NOW ) );
//-----------------------------------------
// Notifications library
//-----------------------------------------
if ( $fromMemberData['member_id'] != $toMember['member_id'] )
{
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
$notifyLibrary = new $classToLoad( $this->registry );
$toMember['language'] = $toMember['language'] == "" ? IPSLib::getDefaultLanguage() : $toMember['language'];
$buildMessage = array( 'NAME' => $toMember['members_display_name'],
'POSTER' => $fromMemberData['members_display_name'],
'TITLE' => $msgTitle,
'TEXT' => $msgContent,
'LINK' => "?app=members&module=messaging§ion=view&do=showConversation&topicID={$msg_topic_id}#msg{$msg_id}" );
IPSText::getTextClass('email')->setPlainTextTemplate( IPSText::getTextClass('email')->getTemplate( 'personal_convo_new_convo', $toMember['language'] ) );
IPSText::getTextClass('email')->buildPlainTextContent( $buildMessage );
IPSText::getTextClass('email')->subject = sprintf(
IPSText::getTextClass('email')->subject,
$this->registry->output->buildSEOUrl( 'showuser=' . $fromMemberData['member_id'], 'public', $fromMemberData['members_seo_name'], 'showuser' ),
$fromMemberData['members_display_name'],
$this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=showConversation&topicID={$msg_topic_id}#msg{$msg_id}", 'public' )
);
$notifyLibrary->setMember( $toMember );
$notifyLibrary->setFrom( $fromMemberData );
$notifyLibrary->setNotificationKey( 'new_private_message' );
$notifyLibrary->setNotificationUrl( $this->registry->output->buildUrl( "app=members&module=messaging§ion=view&do=showConversation&topicID={$msg_topic_id}#msg{$msg_id}", 'public' ) );
$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->getPlainTextContent() );
$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
$notifyLibrary->setNotificationHtml( IPSText::getTextClass('email')->buildHtmlContent( $buildMessage ) );
$notifyLibrary->setMetaData( array( 'meta_area' => 'pm', 'meta_id' => $msg_topic_id, 'meta_app' => 'members' ) );
try
{
$notifyLibrary->sendNotification();
}
catch( Exception $e )
{
}
/**
* @see http://community.invisionpower.com/resources/bugs.html/_/ip-board/private-message-notficiation-counter-stuck-at-1-r42762
*/
IPSMember::save( $toMember['member_id'], array( 'core' => array( 'msg_count_total'=> 'plus:1', 'msg_count_new' => 'plus:1'/*, 'msg_count_reset' => 1*/ ) ) );
}
else
{
IPSMember::save( $toMember['member_id'], array( 'core' => array( 'msg_count_total'=> 'plus:1' ) ) );
}
}
}
else
{
//-----------------------------------------
// Is a draft
//-----------------------------------------
/* Create user map entry */
$this->DB->insert( 'message_topic_user_map', array( 'map_user_id' => $fromMemberData['member_id'],
'map_topic_id' => $msg_topic_id,
'map_folder_id' => 'drafts',
'map_user_active' => 1,
'map_has_unread' => 0,
'map_user_banned' => 0,
'map_read_time' => 0,
'map_left_time' => 0,
'map_ignore_notification' => 0,
'map_last_topic_reply' => IPS_UNIX_TIME_NOW ) );
if( ! $options['topicID'] )
{
//-----------------------------------------
// Update profile
//-----------------------------------------
$this->rebuildFolderCount( $fromMemberData['member_id'], array( 'drafts' => 'plus:1' ), TRUE, array( 'core' => array( 'msg_count_total' => 'plus:1' ) ) );
}
}
return TRUE;
}
/**
* Delete messages from a topic
*
* @param array Array of message IDs to remove
* @param int Deleted by member ID
* @return boolean Deleted
*/
public function deleteMessages( $msgIDs=array(), $deletedByMemberID )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$idsToDelete = array();
$topics = array();
$unread = array();
$deletedByMember = IPSMember::load( intval( $deletedByMemberID ), 'all' );
//-----------------------------------------
// Check
//-----------------------------------------
if ( ! is_array( $msgIDs ) or ! count( $msgIDs ) )
{
return FALSE;
}
//-----------------------------------------
// Fetch all posts...
//-----------------------------------------
$this->DB->build( array( 'select' => 'msg.msg_id, msg.msg_topic_id, msg.msg_author_id',
'from' => array( 'message_posts' => 'msg' ),
'where' => 'msg.msg_id IN (' . implode( ',', IPSLib::cleanIntArray( $msgIDs ) ) . ') AND msg.msg_is_first_post != 1',
'add_join' => array( array( 'select' => 'mt.*',
'from' => array( 'message_topics' => 'mt' ),
'where' => 'mt.mt_id=msg.msg_topic_id',
'type' => 'left' ) ) ) );
$this->DB->execute();
while( $msg = $this->DB->fetch() )
{
if ( $this->_conversationCanDelete( $msg, $msg, $deletedByMember ) === TRUE )
{
$idsToDelete[ $msg['msg_id'] ] = $msg['msg_id'];
$topics[ $msg['msg_topic_id'] ] = $msg['msg_topic_id'];
}
}
//-----------------------------------------
// Got anything?
//-----------------------------------------
if ( ! count( $idsToDelete ) )
{
return FALSE;
}
//-----------------------------------------
// Is there an attachment to these messages??
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'core' ) . '/sources/classes/attach/class_attach.php', 'class_attach' );
$class_attach = new $classToLoad( $this->registry );
$class_attach->type = 'msg';
$class_attach->init();
$class_attach->bulkRemoveAttachment( $idsToDelete );
//-----------------------------------------
// Delete the messages
//-----------------------------------------
$this->DB->delete( 'message_posts', 'msg_id IN (' . implode( ',', IPSLib::cleanIntArray( $msgIDs ) ) . ')' );
//-----------------------------------------
// Rebuild member's new message count
// This MUST go before we rebuild the topic
// so we get all those who haven't yet read
// the last replies...
//-----------------------------------------
$this->DB->build( array( 'select' => '*',
'from' => 'message_topic_user_map',
'where' => 'map_user_active=1 AND map_topic_id IN (' . implode( ",", array_keys( $topics ) ) . ') AND map_has_unread=1' ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$unread[ $row['map_user_id'] ] = $row['map_user_id'];
}
//-----------------------------------------
// Update all relevant topics
//-----------------------------------------
foreach( array_keys( $topics ) as $topicID )
{
$this->rebuildTopic( $topicID );
}
/* Update member counts */
if ( count( $unread ) )
{
$this->resetMembersNewTopicCount( $unread );
}
return TRUE;
}
/**
* Rebuild a PM topic
*
* @param int Topic ID
* @return boolean
*/
public function rebuildTopic( $topicID )
{
//-----------------------------------------
// Get message count
//-----------------------------------------
$replyCount = $this->DB->buildAndFetch( array( 'select' => 'count(*) as count',
'from' => 'message_posts',
'where' => 'msg_topic_id=' . $topicID ) );
$replyCount['count'] = ( $replyCount['count'] > 0 ) ? $replyCount['count'] - 1 : 0;
//-----------------------------------------
// Fetch latest msg ID
//-----------------------------------------
$latestID = $this->DB->buildAndFetch( array( 'select' => 'msg_id, msg_date',
'from' => 'message_posts',
'where' => 'msg_topic_id=' . $topicID,
'order' => 'msg_date DESC',
'limit' => array( 0, 1 ) ) );
//-----------------------------------------
// Fetch first msg ID
//-----------------------------------------
$firstID = $this->DB->buildAndFetch( array( 'select' => 'msg_id, msg_date',
'from' => 'message_posts',
'where' => 'msg_topic_id=' . $topicID,
'order' => 'msg_date ASC',
'limit' => array( 0, 1 ) ) );
//-----------------------------------------
// Recount attachments
//-----------------------------------------
$attach = $this->DB->buildAndFetch( array(
'select' => 'COUNT(*) as attachments',
'from' => array( 'attachments' => 'a' ),
'where' => 'm.msg_topic_id=' . $topicID . " AND a.attach_rel_module='msg'",
'add_join' => array(
array( 'from' => array( 'message_posts' => 'm' ),
'where' => 'm.msg_id=a.attach_rel_id'
)
)
) );
//-----------------------------------------
// Update...
//-----------------------------------------
$this->DB->update( 'message_posts', array( 'msg_is_first_post' => 1 ), 'msg_id=' . $firstID['msg_id'] );
$this->DB->update( 'message_topics', array( 'mt_last_post_time' => $latestID['msg_date'],
'mt_replies' => intval( $replyCount['count'] ),
'mt_last_msg_id' => $latestID['msg_id'],
'mt_hasattach' => intval($attach['attachments']),
'mt_first_msg_id' => $firstID['msg_id'] ), 'mt_id=' . $topicID );
$this->DB->update( 'message_topic_user_map', array( 'map_last_topic_reply' => $latestID['msg_date'] ), 'map_topic_id=' . $topicID );
return TRUE;
}
/**
* Generate a 'definitive" list of invite users from an array of names
*
* @param array Array of display names [ 'bob', 'joe', 'dave' ]
* @param array Optional[ Array of member data (group + core only required) ]
* @param int Number of existing participants
* @return array Array of members, indexed by member_id
*
* <code>
* Exception Codes:
* NOT_ALL_INVITE_USERS_EXIST: Not all invite users exist (check $this->exceptionData for a list of names)
* NOT_ALL_INVITE_USERS_CAN_PM: Not all invite users can PM (check $this->exceptionData for a list of names)
* INVITE_USERS_BLOCKED: Some invite users have been blocked (check $this->exceptionData for a list of names)
* </code>
*/
public function checkAndReturnInvitedUsers( $names, $member=array(), $current=0 )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$finalArray = array();
$inviteArrayByID = array();
$inviteArrayByName = array();
$member = ( $member['member_id'] ) ? $member : $this->member->fetchMemberData();
$doesNotExist = array();
$cannotUsePM = array();
$current = intval($current);
if ( ! is_array( $names ) OR ! count( $names ) )
{
return $finalArray;
}
//-----------------------------------------
// Get the members
//-----------------------------------------
$_members = IPSMember::load( $names, 'groups,extendedProfile', 'displayname' );
if ( ! is_array( $_members ) OR ! count( $_members ) )
{
return $finalArray;
}
//-----------------------------------------
// Go Loopy
//-----------------------------------------
foreach( $_members as $member_id => $_member )
{
$inviteArrayByID[ $_member['member_id'] ] = $_member;
$inviteArrayByName[ IPSText::mbstrtolower($_member['members_l_display_name']) ] = $_member['member_id'];
# Only allow the first X based on g_max_mass_pm
if ( count( $inviteArrayByID ) + $current > $member['g_max_mass_pm'] )
{
throw new Exception( "MAX_USER_PM_LIMIT_HIT" );
break;
}
/* Hard limit of 500 */
if( count( $inviteArrayByID ) > 500 )
{
break;
}
}
//-----------------------------------------
// Go loopy again...
//-----------------------------------------
foreach( $names as $name )
{
//-----------------------------------------
// Check to make sure all invite users exist
//-----------------------------------------
if ( ! in_array( IPSText::mbstrtolower( $name ), array_keys( $inviteArrayByName ) ) )
{
$doesNotExist[] = $name;
}
}
if ( count( $doesNotExist ) )
{
$this->exceptionData = $doesNotExist;
throw new Exception( "NOT_ALL_INVITE_USERS_EXIST" );
}
//-----------------------------------------
// Check the block list..
//-----------------------------------------
$blockedInvitedUsers = $this->blockedByUser( $member, array_keys( $inviteArrayByID ) );
if ( count( $blockedInvitedUsers ) )
{
$this->exceptionData = $blockedInvitedUsers;
throw new Exception( "INVITE_USERS_BLOCKED" );
}
//-----------------------------------------
// Check to ensure all invite users can use the PM system
//-----------------------------------------
foreach( $inviteArrayByID as $id => $_member )
{
$_member = $this->_setMaxMessages( $_member );
if ( $_member['g_use_pm'] != 1 OR $_member['members_disable_pm'] )
{
$cannotUsePM[ $id ] = $_member['members_display_name'];
}
if ( $this->withinPMQuota( $_member ) !== TRUE )
{
$cannotUsePM[ $id ] = $_member['members_display_name'];
}
if ( count( $cannotUsePM ) )
{
if ( $this->forceMessageToSend !== TRUE )
{
$this->exceptionData = $cannotUsePM;
throw new Exception( "NOT_ALL_INVITE_USERS_CAN_PM" );
}
else
{
continue;
}
}
$finalArray[ $id ] = $_member;
}
return $finalArray;
}
/**
* Returns number of messages based on folders not a SQL search
*
* @param string ID of folder to get count from
* @param string Folder data line
* @return int Count
*/
public function getFolderTopicCount( $folderID, $folderData )
{
$folderArray = $this->explodeFolderData( $folderData );
return intval( $folderArray[ $folderID ]['count'] );
}
/**
* Rebuild directory count
*
* @param int Member ID
* @param string Folder ID to rebuild
* @param int Count of messages
* @param boolean Flag; do not save to DB [True is default]
* @param array Array of extra data for passing to IPSMember::save()
* @return string Rebuilt 'vdir' string
*/
public function rebuildFolderCount( $memberID, $folderAndCount=array(), $save=TRUE, $extraData=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$rebuild = array();
$dirFolders = '';
//-----------------------------------------
// Got any current directories?
//-----------------------------------------
if ( $memberID == $this->memberData['member_id'] )
{
if ( $this->memberData['pconversation_filters'] )
{
$dirFolders = $this->memberData['pconversation_filters'];
}
}
else
{
$_member = IPSMember::load( $memberID, 'extendedProfile' );
$dirFolders = $_member['pconversation_filters'];
}
//-----------------------------------------
// Parse folder data
//-----------------------------------------
$rebuild = $this->explodeFolderData( $dirFolders );
//-----------------------------------------
// Sort out new count
//-----------------------------------------
foreach( $rebuild as $id => $data )
{
if ( isset($folderAndCount[ $id ]) )
{
$_count = $folderAndCount[ $id ];
if ( strstr( $_count, 'plus:' ) )
{
$newCount = $rebuild[ $data['id'] ]['count'] + intval( str_replace( 'plus:', '', $_count ) );
}
else if ( strstr( $_count, 'minus:' ) )
{
$newCount = $rebuild[ $data['id'] ]['count'] - intval( str_replace( 'minus:', '', $_count ) );
}
else
{
$newCount = $rebuild[ $data['id'] ]['count'] = intval( $_count );
}
$rebuild[ $data['id'] ]['count'] = ( $newCount < 1 ) ? 0 : $newCount;
}
}
$finalString = $this->implodeFolderData( $rebuild );
if ( $save === TRUE )
{
$extraData['extendedProfile']['pconversation_filters'] = $finalString;
IPSMember::save( $memberID, $extraData );
}
/* Update 'us' too.. */
if ( $memberID == $this->memberData['member_id'] )
{
$this->memberData['pconversation_filters'] = $finalString;
}
return $finalString;
}
/**
* Resets alert / count flag
* @param mixed $memberID
*/
public function resetMembersAlertCounts( $memberID )
{
$memberData = ( is_numeric( $memberID ) ) ? IPSMember::load( $memberID, 'extendedProfile' ) : $memberID;
IPSMember::save( $memberData['member_id'], array( 'core' => array( 'msg_count_new' => 0 ) ) );
IPSMember::setToMemberCache( $memberData, array( 'msgAlertReset' => IPS_UNIX_TIME_NOW ) );
}
/**
* Reset all folder counts
*
* @param int Member ID to reset
* @return boolean
*/
public function resetMembersFolderCounts( $memberID )
{
$memberData = IPSMember::load( $memberID, 'extendedProfile' );
/* Grab folders */
$folders = $this->explodeFolderData( $memberData['pconversation_filters'] );
/* Grab counts */
$counts = $this->getPersonalTopicsCount( $memberData['member_id'] );
/* Zero 'em out */
foreach( $folders as $folderID => $data )
{
$folders[ $folderID ]['count'] = 0;
}
if ( is_array( $counts ) )
{
foreach( $counts as $folderID => $count )
{
$folders[ $folderID ]['count'] = intval( $count );
}
}
/* Generate */
$dirs = $this->implodeFolderData( $folders );
/* Save */
IPSMember::save( $memberData['member_id'], array( 'core' => array( 'msg_count_reset' => 0 ),
'extendedProfile' => array( 'pconversation_filters' => $dirs ) ) );
/* Reset our counter */
if ( $memberID == $this->memberData['member_id'] )
{
$this->memberData['msg_count_reset'] = 0;
}
return $dirs;
}
/**
* Resets a user(s) new message count
*
* @param mixed Either INT member ID or ARRAY of member IDs
* @return boolean
*/
public function resetMembersNewTopicCount( $member )
{
//-----------------------------------------
// Load members...
//-----------------------------------------
if ( is_array( $member ) )
{
$members = IPSMember::load( $member, 'extendedProfile' );
}
else
{
$_member = IPSMember::load( $member, 'extendedProfile' );
$members = array( $_member['member_id'] => $_member );
}
//-----------------------------------------
// Loop 'em
//-----------------------------------------
foreach( $members as $id => $data )
{
$count = intval( $this->getPersonalTopicsCount( $id, 'new' ) );
$lastReset = intval( IPSMember::getFromMemberCache( $data, 'msgAlertReset' ) );
$msgAlertCount = intval( $this->getPersonalTopicsCount( $id, 'new', $lastReset ) );
//-----------------------------------------
// Parse folder data
//-----------------------------------------
$rebuild = $this->explodeFolderData( $data['pconversation_filters'] );
$rebuild['new']['count'] = $count;
$msg_show_notification = ( $count ) ? $data['msg_show_notification'] : 0;
IPSMember::save( $id, array( 'extendedProfile' => array( 'pconversation_filters' => $this->implodeFolderData( $rebuild ) ),
'core' => array( 'msg_count_new' => $msgAlertCount,
'msg_show_notification' => $msg_show_notification ) ) );
IPSMember::setToMemberCache( $data, array( 'msgAlertReset' => IPS_UNIX_TIME_NOW ) );
/* Reset our counter */
if ( $id == $this->memberData['member_id'] )
{
$this->memberData['msg_count_new'] = $msgAlertCount;
}
}
return TRUE;
}
/**
* Resets a members total message count
*
* @param int Member ID
* @return int Message count
*/
public function resetMembersTotalTopicCount( $memberID )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$memberID = intval( $memberID );
//-----------------------------------------
// Grab count
//-----------------------------------------
$total = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as msgTotal',
'from' => 'message_topic_user_map',
'where' => "map_user_id=".$memberID." AND map_user_active=1 AND map_user_banned=0 AND map_is_system=0" ) );
$total['msgTotal'] = intval($total['msgTotal']);
IPSMember::save( $memberID, array( 'core' => array( 'msg_count_total' => $total['msgTotal'] ) ) );
return $total['msgTotal'];
}
/**
* Returns PM count on spec
*
* @param int Member ID
* @param string Folder ID (eg: inbox, sent, etc) [ Optional, if omitted, all msg box counts are returned ]
* @param int Timestamp to check from
* @return mixed If folder ID: Number of messages, otherwise an array of folderID => count
*/
public function getPersonalTopicsCount( $memberID, $folderID='', $time=null )
{
$return = array();
$where = '';
if ( is_numeric( $time ) )
{
$where = ( $folderID == 'drafts' ) ? ' AND mt_date > ' . $time : ' AND map_last_topic_reply > ' . $time;
}
if ( $memberID and $folderID )
{
/* New folder */
if ( $folderID == 'new' )
{
$t = $this->DB->buildAndFetch( array ( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topic_user_map',
'where' => "map_user_active=1 AND map_user_id=" . intval( $memberID ) . " AND map_has_unread=1" . $where ) );
return intval( $t['msg_total'] );
}
/* Drafts */
else if ( $folderID == 'drafts' )
{
$t = $this->DB->buildAndFetch( array ( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topics',
'where' => "mt_starter_id=" . intval( $memberID ) . " AND mt_is_draft=1 AND mt_is_deleted=0" . $where ) );
return intval( $t['msg_total'] );
}
/* All other folders */
else
{
$_folder = $this->DB->addSlashes( $folderID );
if ( $this->_folderFilter )
{
if ( $this->_folderFilter == 'in' )
{
$t = $this->DB->buildAndFetch( array ( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topic_user_map',
'where' => "map_user_id=" . intval( $memberID ) . " AND map_user_active=1 AND map_folder_id='" . $_folder . "' AND map_is_starter=0" . $where ) );
return intval( $t['msg_total'] );
}
else if ( $this->_folderFilter == 'sent' )
{
$t = $this->DB->buildAndFetch( array ( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topic_user_map',
'where' => "map_user_id=" . intval( $memberID ) . " AND map_user_active=1 AND map_folder_id='" . $_folder . "' AND map_is_starter=1" . $where ) );
return intval( $t['msg_total'] );
}
}
else
{
$t = $this->DB->buildAndFetch( array ( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topic_user_map',
'where' => "map_user_id=" . intval( $memberID ) . " AND map_user_active=1 AND map_folder_id='" . $_folder . "'" . $where ) );
return intval( $t['msg_total'] );
}
}
}
else if ( $memberID )
{
$this->DB->build( array( 'select' => 'COUNT(*) as msg_total, map_folder_id',
'from' => 'message_topic_user_map',
'where' => "map_is_system=0 AND map_user_active=1 AND map_user_id=" . intval( $memberID ) . $where,
'group' => 'map_folder_id' ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$return[ $row['map_folder_id'] ] = $row['msg_total'];
}
$return['drafts'] = $this->getPersonalTopicsCount( $memberID, 'drafts' );
$return['new'] = $this->getPersonalTopicsCount( $memberID, 'new' );
return $return;
}
else
{
return 0;
}
}
/**
* Returns PM data based on spec
*
* @param int Member ID
* @param string Folder ID (eg: inbox, sent, etc)
* @param array Array of data ( array[ 'sort' => '', offsetStart' => '', 'offsetEnd' => '' )
* @return array Array of PMs indexed by PM ID
*/
public function getPersonalTopicsList( $memberID, $folderID, $searchAndSort )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$sortKey = '';
$where = '';
$memberID = intval( $memberID );
$_memberIDs = array();
$folderID = IPSText::alphanumericalClean( $folderID, '-_' );
$oStart = intval( $searchAndSort['offsetStart'] );
$oEnd = intval( $searchAndSort['offsetEnd'] );
$messages = array();
//-----------------------------------------
// Figure out sort key
//-----------------------------------------
switch ( $searchAndSort['sort'] )
{
case 'rdate':
$sortKey = 'mm.map_last_topic_reply ASC';
break;
case 'title':
$sortKey = 'mt.mt_title ASC';
break;
case 'nameTo':
$sortKey = 'mem.members_display_name ASC';
break;
default:
$sortKey = 'mm.map_last_topic_reply DESC';
break;
}
//-----------------------------------------
// Figure out where clause (no, not santa)
//-----------------------------------------
switch( $folderID )
{
default:
if ( $this->_folderFilter )
{
if ( $this->_folderFilter == 'in' )
{
$where = " AND mm.map_user_active=1 AND ( mm.map_folder_id='{$folderID}' AND mm.map_is_starter=0 )";
}
else if ( $this->_folderFilter == 'sent' )
{
$where = " AND mm.map_user_active=1 AND ( mm.map_folder_id='{$folderID}' AND mm.map_is_starter=1)";
}
}
else if ( $folderID != 'all')
{
$where = " AND mm.map_user_active=1 AND mm.map_folder_id='{$folderID}'";
}
else
{
$where = " AND mm.map_user_active=1";
}
break;
case 'drafts':
$where = " AND mm.map_user_active=1 AND mt.mt_is_draft=1";
break;
case 'new':
$where = ' AND mm.map_user_active=1 AND mm.map_has_unread=1';
break;
}
if ( ! $memberID or ! $folderID )
{
return $messages;
}
else
{
$this->DB->build( array( 'select' => 'mm.*',
'from' => array( 'message_topic_user_map' => 'mm' ),
'where' => "mm.map_user_id=" . $memberID . $where,
'order' => $sortKey,
'limit' => array( $oStart, $oEnd ),
'add_join' => array(
array( 'select' => 'mt.*',
'from' => array( 'message_topics' => 'mt' ),
'where' => 'mm.map_topic_id=mt.mt_id',
'type' => 'left'
),
array( 'select' => 'msg.*',
'from' => array( 'message_posts' => 'msg' ),
'where' => 'msg.msg_id=mt.mt_last_msg_id',
'type' => 'left'
),
array( 'select' => 'mem.members_display_name',
'from' => array( 'members' => 'mem' ),
'where' => 'mt.mt_to_member_id=mem.member_id',
'type' => 'left' ) ) ) );
$this->DB->execute();
//-----------------------------------------
// Get the messages
//-----------------------------------------
while( $row = $this->DB->fetch() )
{
$_toID = intval( $row['mt_to_member_id'] );
$_startID = intval( $row['mt_starter_id'] );
$_lastID = intval( $row['msg_author_id'] );
/* Add member IDs */
$_memberIDs[ $_toID ] = $_toID;
$_memberIDs[ $_startID ] = $_startID;
$_memberIDs[ $_lastID ] = $_lastID;
/* Invited users */
if ( $row['mt_invited_members'] )
{
$row['_invitedMembers'] = unserialize( $row['mt_invited_members'] );
if ( is_array( $row['_invitedMembers'] ) AND count( $row['_invitedMembers'] ) )
{
foreach ( $row['_invitedMembers'] as $_mid )
{
$_memberIDs[ $_mid ] = $_mid;
}
}
}
/* Pagination */
if ( ( ($row['mt_replies'] + 1 ) % $this->messagesPerPage ) == 0 )
{
$pages = ($row['mt_replies'] + 1) / $this->messagesPerPage;
}
else
{
$number = ( ($row['mt_replies'] + 1) / $this->messagesPerPage );
$pages = ceil( $number);
}
if ( $pages > 1 )
{
for ( $i = 0 ; $i < $pages ; ++$i )
{
$real_no = $i * $this->messagesPerPage;
$page_no = $i + 1;
if ( $page_no == 4 and $pages > 4 )
{
$row['pages'][] = array( 'last' => 1,
'st' => ($pages - 1) * $this->messagesPerPage,
'page' => $pages );
break;
}
else
{
$row['pages'][] = array( 'last' => 0,
'st' => $real_no,
'page' => $page_no );
}
}
}
/* The rest */
$row['_folderName'] = $this->_dirData[ $row['map_folder_id'] ]['real'];
$row['_otherInviteeCount'] = intval( $row['mt_to_count'] - 1 );
$messages[ $row['mt_id'] ] = $row;
}
$members = array();
/* Got any members? */
if ( count( $_memberIDs ) )
{
$members = IPSMember::load( $_memberIDs, 'all' );
}
/* Let's sort out the ignore & photos at once for everyone */
if( count($members) )
{
foreach( $members as $_mid => $_mdata )
{
$_mdata['_canBeBlocked'] = IPSMember::isIgnorable( $_mdata['member_group_id'], $_mdata['mgroup_others'], 'pm' );
$members[ $_mid ] = IPSMember::buildProfilePhoto( $_mdata );
}
}
$members[ 0 ] = IPSMember::buildProfilePhoto( array( 'members_display_name' => $this->lang->words['deleted_user'] ) );
/* Now merge (in turn!) */
foreach( $messages as $id => $row )
{
/* From */
$messages[ $id ]['_starterMemberData'] = $members[ $row['mt_starter_id'] ];
/* To */
$messages[ $id ]['_toMemberData'] = $members[ $row['mt_to_member_id'] ];
/* Last msg author */
$messages[ $id ]['_lastMsgAuthor'] = $members[ $row['msg_author_id'] ];
/* Invitees */
if ( is_array( $row['_invitedMembers'] ) AND count( $row['_invitedMembers'] ) )
{
foreach ( $row['_invitedMembers'] as $_mid )
{
$messages[ $id ]['_invitedMemberData'][ $_mid ] = $members[ $_mid ];
$messages[ $id ]['_invitedMemberNames'][] = $members[ $_mid ]['members_display_name'];
}
}
else
{
$row['_invitedMembers'] = array();
$messages[ $id ]['_invitedMemberData'] = array();
$messages[ $id ]['_invitedMemberNames'] = array();
}
}
}
return $messages;
}
/**
* Returns a complete topic
*
* @param int Member ID
* @param string Folder ID (eg: inbox, sent, etc)
* @param array Array of data ( array[ sort => '', offsetStart' => '', 'offsetEnd' => '' )
* @return array Array of PMs indexed by PM ID
*
* <code>
* Exception Codes
* NO_READ_PERMISSION You do not have permission to read the topic
* YOU_ARE_BANNED You have been banned
* </code>
*/
public function fetchConversation( $topicID, $readingMemberID, $filters=array() )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$readingMemberID = intval( $readingMemberID );
$topicID = intval( $topicID );
$oStart = intval( $filters['offsetStart'] );
$oEnd = intval( $filters['offsetEnd'] );
$replyData = array();
$topicData = array();
$remapData = array();
$memberData = array();
$missingMembers = array();
$whereExtra = '';
//-----------------------------------------
// Figure out sort key
//-----------------------------------------
switch ( $filters['sort'] )
{
case 'rdate':
$sortKey = 'msg.msg_date DESC';
break;
default:
$sortKey = 'msg.msg_date ASC';
break;
}
if ( ! $topicID )
{
return array( 'topicData' => $topicData, 'replyData' => $replyData );
}
else
{
/* Get member data */
$memberData = $this->fetchTopicParticipants( $topicID, TRUE );
/* Get reading member's data */
$readingMemberData = $memberData[ $readingMemberID ];
/* Fetch topic data */
$topicData = $this->fetchTopicData( $topicID, FALSE );
/* Topic deleted? Grab topic starter details, as they won't be in the participant array */
if ( $topicData['mt_is_deleted'] AND ( $topicData['mt_starter_id'] > 0 ) )
{
$memberData[ $topicData['mt_starter_id'] ] = IPSMember::load( $topicData['mt_starter_id'], 'all' );
$memberData[ $topicData['mt_starter_id'] ]['_canBeBlocked'] = IPSMember::isIgnorable( $memberData[ $topicData['mt_starter_id'] ]['member_group_id'], $memberData[ $topicData['mt_starter_id'] ]['mgroup_others'], 'pm' );
$memberData[ $topicData['mt_starter_id'] ] = IPSMember::buildDisplayData( $memberData[ $topicData['mt_starter_id'] ], array( '__all__' => 1 ) );
$memberData[ $topicData['mt_starter_id'] ]['map_user_active'] = 1;
/* Set flag for topic participant starter */
$memberData[ $topicData['mt_starter_id'] ]['map_is_starter'] = 1;
foreach( $memberData as $id => $data )
{
$memberData[ $id ]['_topicDeleted'] = 1;
}
}
/* Can access this topic? */
if ( $this->canAccessTopic( $readingMemberID, $topicData, $memberData ) !== TRUE )
{
/* Banned? */
if ( $readingMemberData['map_user_banned'] )
{
throw new Exception( "YOU_ARE_BANNED" );
}
else
{
throw new Exception( "NO_READ_PERMISSION" );
}
}
/* Reply Data */
$this->DB->build( array( 'select' => 'msg.*',
'from' => array( 'message_posts' => 'msg' ),
'where' => "msg.msg_topic_id=" . $topicID . $whereExtra,
'order' => $sortKey,
'limit' => array( $oStart, $oEnd ),
'add_join' => array(
array( 'select' => 'iu.*',
'from' => array( 'ignored_users' => 'iu' ),
'where' => 'iu.ignore_owner_id=' . $readingMemberID . ' AND iu.ignore_ignore_id=msg.msg_author_id',
'type' => 'left' ),
array( 'select' => 'm.member_group_id, m.mgroup_others',
'from' => array( 'members' => 'm' ),
'where' => 'm.member_id=msg.msg_author_id',
'type' => 'left' ),
)
) );
$o = $this->DB->execute();
//-----------------------------------------
// Get the messages
//-----------------------------------------
while( $msg = $this->DB->fetch( $o ) )
{
$msg['_ip_address'] = "";
/* IP Address */
if ( $msg['msg_ip_address'] AND $readingMemberData['g_is_supmod'] == 1 )
{
$msg['_ip_address'] = $msg['msg_ip_address'];
}
/* Edit */
$msg['_canEdit'] = $this->_conversationCanEdit( $msg, $topicData, $readingMemberData );
/* Delete */
$msg['_canDelete'] = $this->_conversationCanDelete( $msg, $topicData, $readingMemberData );
/* Format Message */
$msg['msg_post'] = $this->_formatMessageForDisplay( $msg['msg_post'], $msg );
/* Member missing? */
if ( ! isset($memberData[ $msg['msg_author_id'] ]) )
{
if( !$msg['msg_author_id'] )
{
$memberData[0] = array();
}
else
{
$missingMembers[ $msg['msg_author_id'] ] = $msg['msg_author_id'];
}
}
else
{
if ( ! empty( $memberData[ $msg['msg_author_id'] ]['signature'] ) )
{
if ( ! $this->memberData['view_sigs'] || ( $msg['msg_author_id'] && $this->memberData['member_id'] && ! empty( $this->member->ignored_users[ $msg['msg_author_id'] ]['ignore_signatures'] ) ) )
{
$memberData[ $msg['msg_author_id'] ]['signature'] = '';
}
}
}
$replyData[ $msg['msg_id'] ] = $msg;
}
}
/* Members who've deleted a closed conversation? */
if ( count( $missingMembers ) )
{
$_members = IPSMember::load( array_keys( $missingMembers ), 'all' );
foreach( $_members as $id => $data )
{
$data['_canBeBlocked'] = IPSMember::isIgnorable( $memberData[ $topicData['mt_starter_id'] ]['member_group_id'], $memberData[ $topicData['mt_starter_id'] ]['mgroup_others'], 'pm' );
$data['map_user_active'] = 0;
$memberData[ $data['member_id'] ] = IPSMember::buildDisplayData( $data, array( '__all__' => 1 ) );
}
}
/* Update reading member's read time */
$this->DB->update( 'message_topic_user_map', array( 'map_read_time' => time(), 'map_has_unread' => 0 ), 'map_user_id=' . intval($readingMemberData['member_id']) . ' AND map_topic_id=' . $topicID );
/* Reduce the number of 'new' messages */
$_newMsgs = intval( $this->getPersonalTopicsCount( $readingMemberID, 'new') );
if ( $memberData[ $readingMemberID ]['map_has_unread'] )
{
$lastReset = intval( IPSMember::getFromMemberCache( $memberData[ $readingMemberID ], 'msgAlertReset' ) );
$msgAlertCount = intval( $this->getPersonalTopicsCount( $readingMemberID, 'new', $lastReset ) );
$_pc = $this->rebuildFolderCount( $readingMemberID, array( 'new' => $_newMsgs ), TRUE );
IPSMember::save( $readingMemberID, array( 'core' => array( 'msg_count_new' => $msgAlertCount, 'msg_show_notification' => 0 ) ) );
/* is this us? */
if ( $readingMemberID == $this->memberData['member_id'] )
{
/* Reset folder data */
$this->_dirData = $this->explodeFolderData( $_pc );
/* Reset global new count */
$this->memberData['msg_count_new'] = $msgAlertCount;
}
}
/* Clean up topic title */
$topicData['mt_title'] = str_replace( '[attachmentid=', '[attachmentid=', $topicData['mt_title'] );
/* Flag externals as read */
$this->_flagAsReadForExternals( array( $topicID ) );
/* Ensure our read time is updated */
$memberData[ $readingMemberID ]['map_read_time'] = time();
/* Do we have a deleted user? */
if ( isset( $memberData[0] ) AND $memberData[0]['member_id'] == 0 )
{
$memberData[0] = IPSMember::buildDisplayData(IPSMember::setUpGuest( $this->lang->words['deleted_user'] ), array( '__all__' => 1 ) );
}
//-----------------------------------------
// Attachments?
//-----------------------------------------
if ( $topicData['mt_hasattach'] )
{
//-----------------------------------------
// INIT. Yes it is
//-----------------------------------------
$postHTML = array();
//-----------------------------------------
// Separate out post content
//-----------------------------------------
foreach( $replyData as $id => $post )
{
$postHTML[ $id ] = $post['msg_post'];
}
if ( ! is_object( $this->class_attach ) )
{
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'core' ) . '/sources/classes/attach/class_attach.php', 'class_attach' );
$this->class_attach = new $classToLoad( $this->registry );
}
$this->class_attach->type = 'msg';
$this->class_attach->init();
$attachHTML = $this->class_attach->renderAttachments( $postHTML );
/* Now parse back in the rendered posts */
foreach( $attachHTML as $id => $data )
{
/* Get rid of any lingering attachment tags */
if ( stristr( $data['html'], "[attachment=" ) )
{
$data['html'] = IPSText::stripAttachTag( $data['html'] );
}
$replyData[ $id ]['msg_post'] = $data['html'];
$replyData[ $id ]['attachmentHtml'] = $data['attachmentHtml'];
}
}
/* Check to make sure we're not the only one left with this topic */
$active = 0;
foreach( $memberData as $id => $data )
{
if ( $id !== $readingMemberID )
{
if ( $data['map_user_active'] )
{
$active++;
break; // 1 active is enough
}
}
}
if ( ! $active )
{
$topicData['_everyoneElseHasLeft'] = 1;
}
/* Return */
return array( 'topicData' => $topicData, 'replyData' => $replyData, 'memberData' => $memberData );
}
/**
* "Implode" folder string
*
* @param array Array indexed by 'id' => [ 'id' => string, 'real' => string, 'count' => int ]
* @return string Formed folder string
*/
public function implodeFolderData( $folderArray )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$return = array();
if ( ! is_array( $folderArray ) )
{
return serialize( $this->_fetchDefaultFolders() );
}
else
{
return serialize( $folderArray );
}
}
/**
* "Explodes" folder string
*
* @param string Raw folder data
* @return array Array indexed by 'id' => [ 'id' => string, 'real' => string, 'count' => int ]
*/
public function explodeFolderData( $folderData )
{
//-----------------------------------------
// Set up default pconversation_filters
//-----------------------------------------
if ( ! $folderData )
{
$folderData = $this->_fetchDefaultFolders();
}
else
{
$folderData = unserialize( $folderData );
}
/* Fallback */
if ( !is_array( $folderData ) )
{
$folderData = $this->_fetchDefaultFolders();
}
//-----------------------------------------
// Process...
//-----------------------------------------
foreach( $folderData as $id => $data )
{
/* Language key exists? */
if ( isset( $this->lang->words[ 'msgFolder_' . $data['id'] ] ) )
{
$folderData[ $id ]['real'] = $this->lang->words[ 'msgFolder_' . $data['id'] ];
}
}
return $folderData;
}
/**
* Have we been blocked?
*
* @param array Person A: Array of member data (core + groups required) OR member ID
* @param array [Array of] member IDs to check 'person A' has been blocked by them. Array of member data (core + groups required) OR member ID OR array of IDs
* @return array
*/
public function blockedByUser( $fromMember, $toMember )
{
$fromMember = ( is_array( $fromMember ) ) ? $fromMember : IPSMember::load( intval( $fromMember ), 'groups,extendedProfile' );
$toMembers = array();
$blockedBy = array();
if ( is_array( $toMember ) AND ( $toMember['member_id'] != '' ) )
{
$toMembers = array( $toMember['member_id'] => $toMember );
}
else if ( is_array( $toMember ) AND count( $toMember ) )
{
$toMembers = IPSMember::load( $toMember, 'groups,extendedProfile' );
}
else
{
$toMembers = array( $toMember => IPSMember::load( intval( $toMember ), 'groups,extendedProfile' ) );
}
$justToIDs = array_keys( $toMembers );
//-----------------------------------------
// Can anyone block us?
//-----------------------------------------
if ( $fromMember['_canBeIgnored'] !== TRUE )
{
return $blockedBy;
}
//-----------------------------------------
// Grab the data from the ignored table
//-----------------------------------------
$this->DB->build( array( 'select' => '*',
'from' => 'ignored_users',
'where' => 'ignore_messages=1 AND ignore_owner_id IN ( ' . implode( ',', $justToIDs ) . ' ) AND ignore_ignore_id=' . $fromMember['member_id'] ) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
if ( in_array( $row['ignore_owner_id'], $justToIDs ) )
{
$blockedBy[ $row['ignore_owner_id'] ] = $row['ignore_owner_id'];
}
}
return $blockedBy;
}
/**
* Can we use the PM system?
*
* @param array Array of member data (core + groups required) OR member ID
* @return boolean TRUE is OK
*/
public function canUsePMSystem( $toMember )
{
$toMember = ( is_array( $toMember ) ) ? $toMember : IPSMember::load( intval( $toMember ), 'groups,extendedProfile' );
/* Not using PM system? */
if ( $toMember['members_disable_pm'] )
{
return FALSE;
}
if ( $toMember['mgroup_others'] && ! $toMember['g_use_pm'] )
{
$groups_id = explode( ',', $toMember['mgroup_others'] );
$cache = $this->caches['group_cache'];
if ( count( $groups_id ) )
{
foreach( $groups_id as $pid )
{
if ( ! $cache[ $pid ]['g_id'] )
{
continue;
}
if ( $cache[ $pid ]['g_use_pm'] )
{
$toMember['g_use_pm'] = 1;
break;
}
}
}
}
return ( $toMember['g_use_pm'] != 1 ) ? FALSE : TRUE;
}
/**
* Get invite users
* Does this PM have any invite users attached? Mainly used to detect IPB 3.0.0 methods
* over previous methods
*
* @param string Raw data from DB (msg_cc_users)
* @return array Array of IDs
*/
public function getInvitedUsers( $msg_invite_users )
{
if ( ! $msg_invite_users )
{
return array();
}
$_users = ( is_array( $msg_invite_users ) ) ? $msg_invite_users : unserialize( $msg_invite_users );
if ( ! count( $_users ) )
{
return array();
}
if ( intval( $_users[0] ) == $_users[0] )
{
return $_users;
}
else
{
# Assume they're names, then...
$members = IPSMember::load( $_users, 'core', 'displayname' );
# Just return the IDs
return array_keys( $members );
}
}
/**
* Have we exceeded our PM quota?
*
* @param array Array of member data (core + groups required) OR member ID
* @return boolean TRUE is OK (within quota)
*/
public function withinPMQuota( $member )
{
$member = ( is_array( $member ) ) ? $member : IPSMember::load( intval( $member ), 'groups,extendedProfile' );
$member = $this->_setMaxMessages( $member );
if ( $member['g_max_messages'] > 0 AND ( $member['msg_count_total'] + 1 > $member['g_max_messages'] ) )
{
if ( $this->settings['override_inbox_full'] )
{
$override = array();
$override = explode( ",", $this->settings['override_inbox_full'] );
$do_override = 0;
$my_groups = array( $this->memberData['member_group_id'] );
if ( $this->memberData['mgroup_others'] )
{
$my_groups = array_merge( $my_groups, explode( ",", $this->memberData['mgroup_others'] ) );
}
foreach( $my_groups as $member_group )
{
if ( in_array( $member_group, $override ) )
{
$do_override = 1;
}
}
if ( $do_override == 0 )
{
return FALSE;
}
else
{
return TRUE;
}
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
/**
* Returns the max PMs allowed based on primary and seconday groups
*
* @param array Array of member data
* @return array Member data
*/
protected function _setMaxMessages( $member )
{
$groups_id = explode( ',', $member['mgroup_others'] );
$groupCache = $this->caches['group_cache'];
if ( count( $groups_id ) )
{
foreach( $groups_id as $pid )
{
if ( empty($groupCache[ $pid ]['g_id']) )
{
continue;
}
if ( $member['g_max_messages'] > 0 AND $groupCache[ $pid ]['g_max_messages'] > $member['g_max_messages'] )
{
$member['g_max_messages'] = $groupCache[ $pid ]['g_max_messages'];
}
else if ( $groupCache[ $pid ]['g_max_messages'] == 0 )
{
$member['g_max_messages'] = 0;
}
}
}
return $member;
}
/**
* Builds the data for the messenger storage block
*
* @return array
*/
public function buildMessageTotals()
{
/* Get the number of messages we have in total. */
$total = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as msg_total',
'from' => 'message_topic_user_map',
'where' => "map_user_active=1 AND map_is_system=0 AND map_user_banned=0 AND map_user_id=" . $this->memberData['member_id'] ) );
$totals['msg_total'] = intval( $total['msg_total'] );
/* if we're not in myconvo with a filter */
if ( ! $this->_folderFilter )
{
/* Update the message count if needed */
if ( $totals['msg_total'] != $this->memberData['msg_count_total'] )
{
IPSMember::save( $this->memberData['member_id'], array( 'core' => array( 'msg_count_total' => $totals['msg_total'] ) ) );
}
}
/* Make sure we've not exceeded our alloted allowance. */
$info['full_messenger'] = "<br />";
$info['img_width'] = 1;
$info['vid'] = $this->_currentFolderID;
$info['full_percent'] = '';
$info['amount_info'] = sprintf( $this->lang->words['pmpc_info_string'], $total['msg_total'] ,$this->lang->words['pmpc_unlimited'] );
if ( $this->memberData['g_max_messages'] > 0 )
{
$totals['amount_info'] = sprintf( $this->lang->words['pmpc_info_string'], $total['msg_total'] ,$this->memberData['g_max_messages'] );
$totals['full_percent'] = $total['msg_total'] ? floor( ($total['msg_total'] / $this->memberData['g_max_messages']) * 100) : 0;
$totals['full_percent'] = ( $totals['full_percent'] > 100 ) ? 100 : $totals['full_percent'];
$totals['img_width'] = !empty( $info['full_percent'] ) ? intval($info['full_percent']) * 2.4 : 1;
if ($totals['img_width'] > 300)
{
$totals['img_width'] = 300;
}
if ($totals['msg_total'] >= $this->memberData['g_max_messages'])
{
$totals['full_messenger'] = "<span class='highlight'>".$this->lang->words['c_msg_full']."</span>";
}
else
{
$totals['full_messenger'] = str_replace( "<#PERCENT#>", $info['full_percent'], $this->lang->words['pmpc_full_string'] );
}
}
return $totals;
}
/**
* Test to see whether or not we are allowed in this PM topic
*
* @param int Member ID to test against
* @param mixed Topic ID or array of topic data
* @param array [ topic participants. If omitted, participants are loaded ]
* @param boolean Do not strip non-active users
* @return boolean
*/
public function canAccessTopic( $memberID, $topicData, $topicParticipants=NULL, $noStrip=FALSE )
{
$topicData = ( is_array( $topicData ) ) ? $topicData : $this->fetchTopicData( $topicData );
$topicParticipants = ( is_array( $topicParticipants ) ) ? $topicParticipants : $this->fetchTopicParticipants( $topicData['mt_id'] );
/* Is it deleted? Check this before stripped map_is_active=0 users */
if ( $topicData['mt_is_deleted'] AND isset($topicParticipants[ $memberID ]) )
{
return TRUE;
}
/* Is it a system PM? */
if ( $topicData['mt_is_system'] AND $topicData['mt_starter_id'] == $memberID )
{
return FALSE;
}
if ( defined('FROM_REPORT_CENTER') AND FROM_REPORT_CENTER )
{
return TRUE;
}
/* Now remove all 'non-active' participants */
$topicParticipants = ( $noStrip === FALSE ) ? $this->_stripNonActiveParticipants( $topicParticipants ) : $topicParticipants;
/* Are we in the participants list? */
if ( isset($topicParticipants[ $memberID ]) )
{
if ( $topicParticipants[ $memberID ]['map_user_banned'] )
{
return FALSE;
}
else
{
return TRUE;
}
}
else
{
return FALSE;
}
}
/**
* Determines whether the conversation can be replied to
*
* @param int Member ID to test against
* @param mixed Topic ID or array of topic data
* @param array [ topic participants. If omitted, participants are loaded ]
* @return boolean
*/
public function canReplyTopic( $memberID, $topicData, $topicParticipants=NULL )
{
$topicData = ( is_array( $topicData ) ) ? $topicData : $this->fetchTopicData( $topicData );
$topicParticipants = ( is_array( $topicParticipants ) ) ? $topicParticipants : $this->fetchTopicParticipants( $topicData['mt_id'] );
if ( $this->canAccessTopic( $memberID, $topicData, $topicParticipants ) !== TRUE )
{
return FALSE;
}
/* Is it a system PM? */
if ( $topicData['mt_is_system'] )
{
return FALSE;
}
/* Is it a system PM? */
if ( $topicData['mt_is_deleted'] )
{
return FALSE;
}
/* Ok, then */
return TRUE;
}
/**
* Find the previous PID
*
* @param int Msg ID
* @return int Previous Msg ID
*/
public function fetchPreviousMsgID( $msgID )
{
$prevID = $this->DB->buildAndFetch( array( 'select' => 'MAX(msg_id) as max',
'from' => 'message_posts',
'where' => 'msg_id < ' . $msgID ) );
return intval( $prevID['max'] );
}
/**
* Fetch Message Data
*
* @param int Topic ID
* @param int Msg ID
* @param boolean Load starter member data with it
* @return array Message data
*/
public function fetchMessageData( $topicID, $msgID, $loadMember=FALSE )
{
$msgData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'message_posts',
'where' => 'msg_topic_id=' . intval( $topicID ) . ' AND msg_id=' . intval( $msgID ) ) );
if ( $loadMember AND $msgData['msg_author_id'] )
{
$memberData = IPSMember::load( $msgData['msg_author_id'], 'all' );
$memberData['_canBeBlocked'] = IPSMember::isIgnorable( $memberData['member_group_id'], $memberData['mgroup_others'], 'pm' );
$memberData = IPSMember::buildDisplayData( $memberData, array( '__all__' => 1 ) );
$msgData = array_merge( $msgData, $memberData );
}
return $msgData;
}
/**
* Fetch Message Data by ID
*
* @param int Msg ID
* @param boolean Load starter member data with it
* @return array Message data
*/
public function fetchMessageDataById( $msgID, $loadMember=FALSE )
{
$msgData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'message_posts',
'where' => 'msg_id=' . intval( $msgID ) ) );
if ( $loadMember AND $msgData['msg_author_id'] )
{
$memberData = IPSMember::load( $msgData['msg_author_id'], 'all' );
$memberData['_canBeBlocked'] = IPSMember::isIgnorable( $memberData['member_group_id'], $memberData['mgroup_others'], 'pm' );
$memberData = IPSMember::buildDisplayData( $memberData, array( '__all__' => 1 ) );
$msgData = array_merge( $msgData, $memberData );
}
return $msgData;
}
/**
* Fetch topic Data with first message information
*
* @param int Topic ID
* @param boolean Load starter member data with it
* @return array Topic + message data
*/
public function fetchTopicDataWithMessage( $topicID, $loadMember=FALSE )
{
$topicData = $this->DB->buildAndFetch( array( 'select' => 'mt.*',
'from' => array( 'message_topics' => 'mt' ),
'where' => 'mt.mt_id=' . intval( $topicID ),
'add_join' => array( array( 'select' => 'm.*',
'from' => array( 'message_posts' => 'm' ),
'where' => 'm.msg_id=mt.mt_first_msg_id',
'type' => 'inner' ) ) ) );
if ( $loadMember )
{
$memberData = IPSMember::load( $topicData['mt_starter_id'], 'all' );
$memberData['_canBeBlocked'] = IPSMember::isIgnorable( $memberData['member_group_id'], $memberData['mgroup_others'], 'pm' );
$memberData = IPSMember::buildDisplayData( $memberData, array( '__all__' => 1 ) );
$topicData = is_array($topicData) ? array_merge( $topicData, $memberData ) : $memberData;
}
return $topicData;
}
/**
* Fetch topic Data
*
* @param int Topic ID
* @param boolean Load starter member data with it
* @return array Topic data
*/
public function fetchTopicData( $topicID, $loadMember=FALSE )
{
$topicData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'message_topics',
'where' => 'mt_id=' . intval( $topicID ) ) );
if ( $loadMember )
{
$memberData = IPSMember::load( $topicData['mt_starter_id'], 'all' );
$memberData['_canBeBlocked'] = IPSMember::isIgnorable( $memberData['member_group_id'], $memberData['mgroup_others'], 'pm' );
$memberData = IPSMember::buildDisplayData( $memberData, array( '__all__' => 1 ) );
$topicData = array_merge( $topicData, $memberData );
}
return $topicData;
}
/**
* Fetch the topic participants
*
* @param int Topic ID
* @param boolean Load and parse member data (TRUE for yes, FALSE for no)
* @return array Array of member data indexed by member ID
*/
public function fetchTopicParticipants( $topicID, $parseThem=FALSE )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$memberData = array();
$remapData = array();
$cachedSigs = array();
//-----------------------------------------
// Grab 'em
//-----------------------------------------
$this->DB->build( array(
'select' => '*',
'from' => 'message_topic_user_map',
'where' => 'map_topic_id=' . intval( $topicID ),
) );
$this->DB->execute();
while( $row = $this->DB->fetch() )
{
$remapData[ $row['map_user_id'] ] = $row;
}
if( !count($remapData) )
{
return array();
}
/* Parse 'em? */
if ( $parseThem === TRUE )
{
/* Grab member data */
$memberData = IPSMember::load( array_keys( $remapData ), 'all' );
foreach( $memberData as $id => $data )
{
$data['cache_content'] = $remapData[ $id ]['cache_content'];
$data['_canBeBlocked'] = IPSMember::isIgnorable( $data['member_group_id'], $data['mgroup_others'], 'pm' );
$memberData[ $id ] = IPSMember::buildDisplayData( $data, array( '__all__' => 1 ) );
$memberData[ $id ] = array_merge( $memberData[ $id ], $remapData[ $id ] );
if ( $data['members_disable_pm'] )
{
$memberData[ $id ]['map_user_active'] = 0;
}
}
$remapData = $memberData;
}
return $remapData;
}
/**
* Strip away non-active participants
*
* @param array Array of current topic participants (as returned by fetchTopicParticipants)
* @return array
*/
protected function _stripNonActiveParticipants( $topicParticipants )
{
$_participants = array();
if ( ! is_array( $topicParticipants ) )
{
return array();
}
foreach( $topicParticipants as $id => $data )
{
if ( $data['map_user_active'] and !$data['members_disable_pm'] )
{
$_participants[ $id ] = $data;
}
}
return $_participants;
}
/**
* Function to format the actual message (applies BBcode, etc)
*
* @param string Raw text
* @param array PM data
* @return string Processed text
*/
protected function _formatMessageForDisplay( $msgContent, $data=array() )
{
/* Load parser */
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' );
$parser = new $classToLoad();
$parser->set( array( 'memberData' => $data,
'parseBBCode' => 1,
'parseArea' => 'pms',
'parseHtml' => 0,
'parseEmoticons' => 1 ) );
return $parser->display( $msgContent );
return $msgContent;
}
/**
* Format Post: Converts BBCode, smilies, etc
*
* @param string Raw Post
* @return string Formatted Post
* @author MattMecham
*/
public function _formatMessageForSaving( $msgContent )
{
//-----------------------------------------
// Load editor
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/editor/composite.php', 'classes_editor_composite' );
$_editor = new $classToLoad();
$_editor->setAllowHtml( false );
$_editor->setLegacyMode( false );
if ( IPS_IS_AJAX )
{
/* Was sent via std editor */
$_editor->setRteEnabled( false );
}
$msgContent = $_editor->process( $msgContent );
if ( IPS_IS_AJAX )
{
$msgContent = nl2br( $msgContent );
}
return $msgContent;
}
/**
* Determines whether the message can be edited
*
* @param array Message data
* @param array Topic data
* @param array Reading member data
* @return bool
*/
protected function _conversationCanEdit( $msg, $topicData, $readingMemberData )
{
if ( $topicData['mt_is_deleted'] )
{
return FALSE;
}
/* Is it a system PM? */
if ( $topicData['mt_is_system'] )
{
return FALSE;
}
if ( ( $msg['msg_author_id'] == $readingMemberData['member_id'] ) OR ( $readingMemberData['g_is_supmod'] == 1 ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Determines whether the message can be deleted
*
* @param array Message data
* @param array Topic data
* @param array Reading member data
* @return bool
*/
protected function _conversationCanDelete( $msg, $topicData, $readingMemberData )
{
if ( $topicData['mt_is_deleted'] )
{
return FALSE;
}
if ( ( $msg['msg_author_id'] == $readingMemberData['member_id'] ) OR ( $readingMemberData['g_is_supmod'] == 1 ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Flag members for a PC count reset
*
* @param array Array of MEMBER ids
* @return boolean
*/
protected function _flagForCountReset( $members )
{
if ( count( $members ) )
{
/* OK, so this is a bit naughty and should really go via IPSMember::save()
however, it would take a fair bit of rewriting to 'fix it' so that it could
save out with multiple IDs. So.... */
$this->DB->update( 'members', array( 'msg_count_reset' => 1 ), 'member_id IN (' . implode( ',', array_keys( $members ) ) . ')' );
}
return TRUE;
}
/**
* Makes attachments permananent
*
* @param string Post Key
* @param int Msg ID
* @param int Topic ID
* @return int
*/
protected function _makeAttachmentsPermanent( $postKey, $msgID, $topicID )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$cnt = array( 'cnt' => 0 );
//-----------------------------------------
// Attachments: Re-affirm...
//-----------------------------------------
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'core' ) . '/sources/classes/attach/class_attach.php', 'class_attach' );
$class_attach = new $classToLoad( $this->registry );
$class_attach->type = 'msg';
$class_attach->attach_post_key = $postKey;
$class_attach->attach_rel_id = $msgID;
$class_attach->init();
$return = $class_attach->postProcessUpload( array( 'mt_id' => $topicID ) );
return intval( $return['count'] );
}
/**
* Flag external stuff
* @param array $data
*/
protected function _flagAsReadForExternals( array $data )
{
/* Notifications */
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
$notifyLibrary = new $classToLoad( $this->registry );
$notifyLibrary->setMember( $this->memberData );
$notifyLibrary->deleteNotificationsAsReadByMetaData( array( 'meta_app' => 'members', 'meta_area' => 'pm', 'meta_id' => $data, 'notify_type_key' => 'new_private_message' ) );
}
/**
* Default folder for people won't don't have any
*
* @return array
*/
protected function _fetchDefaultFolders()
{
return array( 'new' => array( 'id' => 'new',
'real' => $this->lang->words['msgFolder_new'],
'count' => 0,
'protected' => 1 ),
'myconvo' => array( 'id' => 'myconvo',
'real' => $this->lang->words['msgFolder_myconvo'],
'count' => 0,
'protected' => 1 ),
'drafts' => array( 'id' => 'drafts',
'real' => $this->lang->words['msgFolder_drafts'],
'count' => 0,
'protected' => 1 ) );
}
}