* Status Updates Class * Holds various functions * Last Updated: $Date: 2013-02-13 11:25:39 -0500 (Wed, 13 Feb 2013) $ * * * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/company/standards.php#license * @package IP.Board * @author MattMecham * @link http://www.invisionpower.com * @version $Rev: 11985 $ * * Example: Create new status update * * $status = new memberStatus( ipsRegistry::instance() ) * * $status->setAuthor( $this->memberData ); * * if ( $status->canCreate() ) * { * $status->setContent("This is my status update"); * $status->create(); * } * * * Example: Reply to a status update * * $status = new memberStatus( ipsRegistry::instance() ) * * $status->setAuthor( $this->memberData ); * $status->setStatusData( $status->fetchMemberLatest( $memberId ) ); * * if ( $status->canReply() ) * { * $status->setContent("This is my reply"); * $status->reply(); * } * * Both valid * $this->setStatusData( $arrayOfData ); * $this->setStatusData( $statusId ); * * $this->setReplyData( $arrayOfData ); * $this->setReplyData( $replyId ); * */ /** * Rules: * Member and Super Mods can create and delete a member's status * Super mods and members NOT banned, restricted posting, moderated posting, ignored by member can reply. * Super mods, Reply owner and status owner can delete a reply */ class memberStatus { /** * Registry Object Shortcuts * * @var object */ protected $registry; protected $DB; protected $settings; protected $request; protected $lang; protected $member; protected $memberData; protected $cache; protected $caches; /** * Status set via status Id entry * * @var array */ protected $_statusData = array(); /** * Owner data of status set via status Id entry * * @var array */ protected $_statusOwner = array(); /** * Bypass all permission checks * * Use with care, this will allow the class to be used within an API * * @var boolean */ protected $_bypassPermChecks = false; protected $_statusCount = 0; /** * Allowed items to be saved in the get/set array * * @var array */ protected $_internalData = array( 'IsApproved' => 1, 'IsImport' => 0, 'Creator' => 'ipb', 'ExternalUpdates' => array() ); protected $_allowedInternalData = array( 'Author', 'Friend', 'StatusData', 'StatusOwner', 'IsImport', 'Creator', 'ReplyData', 'ReplyOwner', 'ExternalUpdates', 'IsApproved', 'Content' ); /** * CONSTRUCTOR * * @param object Registry * @return @e void */ public function __construct( ipsRegistry $registry ) { /* Make registry objects */ $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(); /* Just to be sure */ $this->setBypassPermissionCheck( false ); /* Just for now */ $this->settings['su_parse_url'] = 1; $this->settings['tc_parse_tags'] = 1; $this->settings['tc_parse_names'] = 1; } /** * Gets the count stored by 'fetch' * @return number */ public function getStatusCount() { return intval( $this->_statusCount ); } /** * Set the bypass permission flag * * @param boolean * @return @e void */ public function setBypassPermissionCheck( $bool=false ) { $this->_bypassPermChecks = ( $bool === true ) ? true : false; } /** * Master check to see if statuses are enabled globally * * @return boolean */ public function isEnabled() { return ( $this->settings['su_enabled'] ) ? TRUE : FALSE; } /** * Is status locked? * * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function isLocked( $status=null ) { $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( $status['status_replies'] >= $this->settings['su_max_replies'] ) { return 3; } return ( $status['status_is_locked'] ) ? $status['status_is_locked'] : 0; } /** * Fetch status actions * Default filters are last 10 status actions from everyone sorted in desc date order. * 'not_theirs' pulls all actions that are not on their own statuses * @param mixed [Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null] * @param array Array of sort/filter data ( member_id [int], status_id [int], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string], 'not_theirs' [0,1] ) */ public function fetchActions( $author=null, $filters=array() ) { $author = ( $author === null ) ? $this->getAuthor() : ( is_array( $author ) ? $author : $this->setAuthor( intval( $author ) ) ); $where = array(); $actions = array(); $mids = array(); if ( isset( $filters['member_id'] ) ) { $where[] = "s.action_member_id=" . intval( $filters['member_id'] ); } if ( isset( $filters['unix_cutoff'] ) ) { $where[] = "s.action_date >=" . intval( $filters['unix_cutoff'] ); } if ( isset( $filters['status_id'] ) ) { $where[] = "s.action_status_id =" . intval( $filters['status_id'] ); } if ( isset( $filters['not_theirs'] ) AND isset( $filters['member_id'] ) AND $filters['not_theirs'] ) { $where[] = "s.action_status_owner !=" . intval( $filters['member_id'] ); } if ( isset( $filters['custom'] ) ) { $where[] = "s.action_custom =" . intval( $filters['custom'] ); } $sort_dir = ( $filters['sort_dir'] == 'asc' ) ? 'asc' : 'desc'; $sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'action_date'; $offset = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0; $limit = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 10; /* Grab them */ $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_actions' => 's' ), 'where' => ( count( $where ) ) ? implode( ' AND ', $where ) : '', 'order' => 's.' . $sort_field . ' ' . $sort_dir, 'limit' => array( $offset, $limit ), 'add_join' => array(array( 'select' => 'u.*', 'from' => array( 'member_status_updates' => 'u' ), 'where' => 'u.status_id=s.action_status_id', 'type' => 'left' ), array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.action_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $o = $this->DB->execute(); while( $row = $this->DB->fetch( $o ) ) { /* Format some data */ $row['action_date_formatted_short'] = $this->registry->getClass('class_localization')->getDate( $row['action_date'], 'SHORT' ); /* Format member */ $row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) ); /* Status owner */ if ( $row['status_member_id'] ) { $mids[ $row['status_member_id'] ] = $row['status_member_id']; } $actions[ $row['action_id'] ] = $row; } /* members? */ if ( count( $mids ) ) { $members = IPSMember::load( $mids ); foreach( $actions as $id => $row ) { if ( $row['status_member_id'] ) { $actions[ $id ]['owner'] = IPSMember::buildDisplayData( $members[ $row['status_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) ); } } } /* Phew */ return $actions; } /** * Fetch status updates * Default filters are last 10 status updates from everyone sorted in desc date order. * The 'latest_only' flag will grab the last 10 member's updates. Otherwise it'll just grab the last 10 updates which could be from 1 or more people * The 'member_id' field will only fetch status updates made by member_id (int) UNLESS type set * The 'owner_id' field will fetch status updates made by author_id (INT) * The 'status_id' field will only fetch status updates made by status_id (int) * The 'friends_only' flag will only fetch status updates from authors friends * The 'type' flag can contain 'all', 'mine', 'theirs' - default is 'mine'. * The 'isApproved' flag can contain true, false or null (true, approved only, false, unapproved only, null both) * The 'ignorePermissions' flag can be set to true (do not verify we can view) or false (pull only statuses we can see based on other filters), defaulting to false * getCount - returns a count of all matches without the limit * * @param mixed [Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null] * @param array Array of sort/filter data ( member_id [int], latest_only [0,1], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string], type [string] ) */ public function fetch( $author=null, $filters=array() ) { $author = ( $author === null ) ? $this->getAuthor() : ( is_array( $author ) ? $author : $this->setAuthor( intval( $author ) ) ); $where = array(); $statuses = array(); $mids = array(); $friends = array(); $authors = array(); /* Set up some filters */ $filters['type'] = ( empty( $filters['type'] ) ) ? 'mine' : $filters['type']; if ( isset( $filters['member_id'] ) ) { $where[] = "s.status_member_id=" . intval( $filters['member_id'] ); if ( $filters['type'] == 'mine' ) { $where[] = "s.status_author_id=" . intval( $filters['member_id'] ); } } if ( isset( $filters['relatedTo'] ) && ! empty( $filters['relatedTo'] ) ) { $where[] = "s.status_member_id=" . intval( $filters['relatedTo'] ) . ' OR s.status_author_id=' . intval( $filters['relatedTo'] ); } if ( isset( $filters['ownerOnly'] ) ) { $where[] = "s.status_author_id=s.status_member_id"; } if ( isset( $filters['status_id'] ) ) { $where[] = "s.status_id=" . intval( $filters['status_id'] ); } if ( isset( $filters['unix_cutoff'] ) ) { $where[] = "s.status_date >=" . intval( $filters['unix_cutoff'] ); } if ( isset( $filters['status_is_latest'] ) ) { $where[] = "s.status_is_latest =" . intval( $filters['status_is_latest'] ); } if ( isset( $filters['isApproved'] ) && $filters['isApproved'] !== null ) { if ( $filters['isApproved'] === true ) { $where[] = "s.status_approved=1"; } else if ( $filters['isApproved'] === false ) { $where[] = "s.status_approved=0"; } } if ( !empty( $filters['friends_only'] ) ) { /* Hard limit to 300 friends */ $this->DB->build( array( 'select' => 'friends_friend_id', 'from' => 'profile_friends', 'where' => 'friends_member_id=' . $author['member_id'], 'limit' => array( 0, 300 ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $friends[ $row['friends_friend_id'] ] = $row['friends_friend_id']; } if ( ! count( $friends ) ) { $friends[0] = 0; } $where[] = "s.status_member_id IN (" . implode( ",", $friends ) . ")"; } /* Don't return statuses we can't see */ if ( !isset( $filters['ignorePermissions'] ) || $filters['ignorePermissions'] != true ) { if ( ! $author['g_is_supmod'] ) { if( IPSMember::isOnModQueue( $author['member_id'] ) ) { $where[] = "s.status_approved=1"; } else { $where[] = "(s.status_approved=1 OR s.status_member_id=" . $author['member_id'] . ')'; } } } $sort_dir = ( isset( $filters['sort_dir'] ) AND $filters['sort_dir'] == 'asc' ) ? 'asc' : 'desc'; $sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'status_date'; $offset = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0; $limit = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 10; $member_id = !empty( $filters['member_id'] ) ? $filters['member_id'] : 0; if ( ! empty( $filters['getCount'] ) ) { $count = $this->DB->buildAndFetch( array( 'select' => 'count(*) as cnt', 'from' => array( 'member_status_updates' => 's' ), 'where' => ( count( $where ) ) ? implode( ' AND ', $where ) : '', 'add_join' => array(array( 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.status_author_id', 'type' => 'left' ), array( 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $this->_statusCount = intval( $count['cnt'] ); } /* Grab them */ $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_updates' => 's' ), 'where' => ( count( $where ) ) ? implode( ' AND ', $where ) : '', 'order' => 's.' . $sort_field . ' ' . $sort_dir, 'limit' => array( $offset, $limit ), 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.status_author_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $o = $this->DB->execute(); while( $row = $this->DB->fetch( $o ) ) { /* Format some data */ $row['status_replies'] = intval( $row['status_replies'] ); $row['status_date_formatted_short'] = $this->registry->getClass('class_localization')->getDate( $row['status_date'], 'SHORT' ); $row['_isLocked'] = $this->isLocked( $row ); $row['_userCanReply'] = $this->canReply( $author, $row, $row ); $row['_canDelete'] = $this->canDeleteStatus( $author, $row ); $row['_canLock'] = $this->canLockStatus( $author, $row ); $row['_canUnlock'] = $this->canUnlockStatus( $author, $row ); $row['_creatorImg'] = $this->creatorImage( $row ); $row['_creatorText'] = $this->creatorText( $row ); $row['_canApprove'] = $this->canApproveStatus( $author, $row ); /* For someone else? */ if ( $row['status_member_id'] != $row['status_author_id'] ) { $authorIds[ $row['status_id'] ] = $row['status_member_id']; $mids[$row['status_member_id']] = $row['status_member_id']; } /* Format member */ $row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) ); /* Replies */ if ( $row['status_replies'] AND strstr( $row['status_last_ids'], 'a:' ) ) { $replies = unserialize( $row['status_last_ids'] ); if ( is_array( $replies ) AND count( $replies ) ) { ksort( $replies ); foreach( $replies as $r ) { $mids[ $r['reply_member_id'] ] = $r['reply_member_id']; $r['reply_date_formatted'] = $this->registry->getClass('class_localization')->getDate( $r['reply_date'], 'SHORT' ); $r['_canDelete'] = $this->canDeleteReply( $author, $r, $row ); $r['reply_status_id'] = $row['status_id']; $row['replies'][ $r['reply_id'] ] = $r; } } } else { $row['replies'] = array(); } $statuses[ $row['status_id'] ] = $row; } /* members? */ if ( count( $mids ) ) { $members = IPSMember::load( $mids ); foreach( $statuses as $id => $row ) { if ( $row['status_member_id'] != $row['status_author_id'] ) { $statuses[ $id ]['owner'] = IPSMember::buildDisplayData( $members[ $row['status_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) ); } if ( is_array( $row['replies'] ) ) { foreach( $row['replies'] as $rid => $r ) { if ( $members[ $r['reply_member_id'] ] ) { if ( ! isset( $members[ $r['reply_member_id'] ]['_done'] ) ) { $members[ $r['reply_member_id'] ] = IPSMember::buildDisplayData( $members[ $r['reply_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) ); $members[ $r['reply_member_id'] ]['_done'] = 1; } $statuses[ $id ]['replies'][ $rid ] = array_merge( $statuses[ $id ]['replies'][ $rid ], $members[ $r['reply_member_id'] ] ); } } } } } /* Phew */ return $statuses; } /** * Fetch all replies to a status * Default filters are sorted on reply_date ASC * * @param mixed [Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null] * @param array Array of sort/filter data ( member_id [int], latest_only [0,1], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string] ) */ public function fetchAllReplies( $status=null, $filters=array() ) { $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); $where = array(); $replies = array(); $sort_dir = ( $filters['sort_dir'] == 'desc' ) ? 'desc' : 'asc'; $sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'reply_date'; $offset = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0; $limit = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 100; /* Grab them */ $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_replies' => 's' ), 'where' => 's.reply_status_id=' . intval( $status['status_id'] ), 'order' => 's.' . $sort_field . ' ' . $sort_dir, 'limit' => array( $offset, $limit ), 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.reply_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $o = $this->DB->execute(); while( $row = $this->DB->fetch( $o ) ) { /* Format some data */ $row['reply_date_formatted'] = $this->registry->getClass('class_localization')->getDate( $row['reply_date'], 'SHORT' ); $row['_canDelete'] = $this->canDeleteReply( $this->getAuthor(), $row, $status ); /* Format member */ $row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) ); $replies[ $row['reply_id'] ] = $row; } /* Phew */ return $replies; } /** * Create a status update for a member * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of member data for owner of status update. If null and StatusOwner empty, getAuthor will be used] * @return array Status information */ public function create( $author=null, $owner=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $_owner = $this->getStatusOwner(); $owner = ( $owner === null ) ? ( ! empty( $_owner['member_id'] ) ? $_owner : $author ) : $owner; $data = array(); if ( $this->canCreate( $author, $owner ) ) { if ( $this->getContent() ) { $content = $this->_cleanContent( $this->getContent() ); $hash = IPSText::contentToMd5( $content ); /* Check for this status update already created */ $test = $this->fetchByHash( $owner['member_id'], $hash ); if ( $test['status_id'] ) { /* Already imported this one */ return FALSE; } $data = array( 'status_member_id' => $owner['member_id'], 'status_author_id' => $author['member_id'], 'status_date' => time(), 'status_content' => $this->_parseContent( $content, $this->_internalData['Creator'] ), 'status_hash' => $hash, 'status_replies' => 0, 'status_author_ip' => $this->member->ip_address, 'status_approved' => ( ( $owner['pp_setting_moderate_comments'] && $owner['member_id'] != $author['member_id'] ) || IPSMember::isOnModQueue( $author['member_id'] ) ) ? 0 : $this->getIsApproved(), 'status_imported' => intval( $this->_internalData['IsImport'] ), 'status_creator' => trim( addslashes( $this->_internalData['Creator'] ) ), 'status_last_ids' => '' ); /* Data Hook Location */ IPSLib::doDataHooks( $data, 'statusUpdateNew' ); $this->DB->insert( 'member_status_updates', $data ); $status_id = $this->DB->getInsertId(); $data['status_id'] = $status_id; if ( $owner['member_id'] != $author['member_id'] ) { $this->_sendCommentNotification( $author, $owner, $data ); } else { $this->_recordAction( 'new', $author, $data ); $this->rebuildOwnerLatest( $owner ); /* Fire off external updates */ $eU = $this->getExternalUpdates(); if ( ! $this->_internalData['IsImport'] AND is_array( $eU ) ) { $this->_triggerExternalUpdates( $eU, $status_id, $owner, $content ); } //----------------------------------------- // Notify owner's friends as configured //----------------------------------------- $friends = array(); if ( $this->settings['friends_enabled'] AND $author['member_id'] == $owner['member_id'] ) { $this->DB->build( array( 'select' => 'friends_member_id, friends_approved', 'from' => 'profile_friends', 'where' => 'friends_friend_id=' . $owner['member_id'] ) ); $this->DB->execute(); while( $_friend = $this->DB->fetch() ) { if ( $_friend['friends_approved'] ) { $friends[ $_friend['friends_member_id'] ] = $_friend['friends_member_id']; } } } if( count($friends) ) { //----------------------------------------- // Notifications library //----------------------------------------- $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' ); $notifyLibrary = new $classToLoad( $this->registry ); $friends = IPSMember::load( $friends ); $statusUrl = $this->registry->output->buildSEOUrl( 'app=members&module=profile&section=status&type=single&status_id=' . $status_id, 'publicNoSession', array( $owner['member_id'], $owner['members_seo_name'] ), 'members_status_single' ); /* Build message as few times as possible */ $ndata = array( 'NAME' => '#friend.name#', 'OWNER' => $owner['members_display_name'], 'STATUS' => $data['status_content'], 'URL' => $this->registry->output->buildSEOUrl( 'app=core&module=usercp&tab=core&area=notifications', 'publicNoSession' ) ); /* Get languages */ $langMessages = array(); foreach( $friends as $friend ) { if ( ! isset( $langMessages[ $friend['language'] ] ) ) { IPSText::getTextClass('email')->getTemplate( 'new_status', $friend['language'] ); IPSText::getTextClass('email')->buildMessage( $ndata ); $_subject = sprintf( IPSText::getTextClass('email')->subject, $this->registry->output->buildSEOUrl( 'showuser=' . $owner['member_id'], 'publicNoSession', $owner['members_seo_name'], 'showuser' ), $owner['members_display_name'], $statusUrl ); $langMessages[ $friend['language'] ] = array( 'subject' => $_subject, 'message' => IPSText::getTextClass('email')->message ); } } foreach( $friends as $friend ) { $message = $langMessages[ $friend['language'] ]['message']; $message = str_replace( '#friend.name#', $friend['members_display_name'], $message ); $notifyLibrary->setMember( $friend ); $notifyLibrary->setFrom( $author ); $notifyLibrary->setNotificationKey( 'friend_status_update' ); $notifyLibrary->setNotificationUrl( $statusUrl ); $notifyLibrary->setNotificationText( $message ); $notifyLibrary->setNotificationTitle( $langMessages[ $friend['language'] ]['subject'] ); try { $notifyLibrary->sendNotification(); } catch( Exception $e ){} } } } } return $data; } return FALSE; } /** * Create a status update for a member * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return array Reply information */ public function reply( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); $data = array(); if ( $this->canReply( $author, $status, $status ) ) { if ( $this->getContent() AND $status['status_id'] ) { $data = array( 'reply_status_id' => $status['status_id'], 'reply_member_id' => $author['member_id'], 'reply_date' => time(), 'reply_content' => $this->_cleanContent( $this->getContent() ) ); /* Data Hook Location */ IPSLib::doDataHooks( $data, 'statusCommentNew' ); $this->DB->insert( 'member_status_replies', $data ); $data['reply_id'] = $this->DB->getInsertId(); $this->_recordAction( 'reply', $author, $status, $data ); $this->rebuildStatus( $status ); $this->rebuildOwnerLatest( $author ); $this->_sendNotification( $author, $status, $data ); } return $data; } return FALSE; } /** * Triggers external postings Twitter, etc * * @param array Update to... * @param int Status ID just posted * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param string [Content to update] * @todo [Future] At some point it could be expanded into a mini framework with plugins */ protected function _triggerExternalUpdates( $updates, $status_id=0, $author=null, $content=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $content = ( $content ) ? $this->_cleanContent( $content ) : $this->_cleanContent( $this->getContent() ); /* Fail safe */ if ( ! $author['member_id'] OR ! $content ) { return false; } $url = $this->registry->output->buildSEOUrl( 'app=members&module=profile&section=status&type=single&status_id=' . $status_id, 'publicNoSession', array( $author['member_id'], $author['members_seo_name'] ), 'members_status_single' ); /* Twitter */ if ( $updates['twitter'] ) { if ( IPSLib::twitter_enabled() AND $author['twitter_id'] ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/twitter/connect.php', 'twitter_connect' ); $twitter = new $classToLoad( $this->registry, $author['twitter_token'], $author['twitter_secret'] ); $twitter->updateStatusWithUrl( $content, $url, FALSE ); } } /* Facebook */ if ( $updates['facebook'] ) { if ( IPSLib::fbc_enabled() AND $author['fb_uid'] ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/facebook/connect.php', 'facebook_connect' ); $facebook = new $classToLoad( $this->registry ); $facebook->updateStatusWithUrl( $content, $url, FALSE ); } } } /** * Send notification * * Emails users of new status replies * Emails owner (if selected) and anyone else who replied (if selected) * * @param array Author array * @param array Status array * @param array Reply array * @return boolean */ protected function _sendNotification( $author, $status, $reply ) { if ( $author['member_id'] AND $status['status_id'] AND $reply['reply_id'] ) { /* Get members */ $members = IPSMember::load( array( $author['member_id'], $reply['reply_member_id'], $status['status_author_id'] ), 'core' ); $_author = $members[ $author['member_id'] ]; $_owner = $members[ $status['status_author_id'] ]; $_replier = $members[ $reply['reply_member_id'] ]; if ( $_author['member_id'] AND $_owner['member_id'] AND $_replier['member_id'] ) { /* Make sure we have the correct language pack */ $this->registry->class_localization->loadLanguageFile( array( 'public_profile' ), 'members', $_owner['language'], true ); /* Init vars */ $_ids = array(); $members = array(); $statusUrl = $this->registry->output->buildSEOUrl( 'app=members&module=profile&section=status&type=single&status_id=' . $status['status_id'], 'publicNoSession', array( $_owner['member_id'], $_owner['members_seo_name'] ), 'members_status_single' ); /* Did the status owner want an email? */ if ( $_owner['member_id'] != $_replier['member_id'] ) { /* Load notifications library */ $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' ); $notifyLibrary = new $classToLoad( $this->registry ); IPSText::getTextClass('email')->getTemplate( 'status_reply', $_owner['language'] ); $data = array( 'NAME' => $_owner['members_display_name'], 'OWNER' => $_owner['members_display_name'], 'POSTER' => $_replier['members_display_name'], 'BLURB' => $this->lang->words['email_blurb_yours'], 'STATUS' => $status['status_content'], 'URL' => $this->registry->output->buildSEOUrl( 'app=core&module=usercp&tab=core&area=notifications', 'publicNoSession' ), 'TEXT' => $reply['reply_content'] ); IPSText::getTextClass('email')->buildMessage( $data ); IPSText::getTextClass('email')->subject = sprintf( IPSText::getTextClass('email')->subject, $this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ), $_replier['members_display_name'], $statusUrl ); $notifyLibrary->setMember( $_owner ); $notifyLibrary->setFrom( $_replier ); $notifyLibrary->setNotificationKey( 'reply_your_status' ); $notifyLibrary->setNotificationUrl( $statusUrl ); $notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message ); $notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject ); try { $notifyLibrary->sendNotification(); } catch( Exception $e ){} } /* Now fetch everyone else who has replied */ $this->DB->build( array( 'select' => 'reply_member_id', 'from' => 'member_status_replies', 'where' => 'reply_status_id=' . $status['status_id'] . ' AND reply_member_id NOT IN (' . intval ( $_replier['member_id'] ) . ',' . intval ( $_owner['member_id'] ) . ')' ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $_ids[ $row['reply_member_id'] ] = $row['reply_member_id']; } if ( count( $_ids ) ) { $members = IPSMember::load( $_ids, 'core' ); } if ( is_array( $members ) AND count( $members ) ) { /* Load notifications library */ $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' ); $notifyLibrary = new $classToLoad( $this->registry ); foreach( $members as $id => $member ) { /* Make sure we have the correct language pack */ $this->registry->class_localization->loadLanguageFile( array( 'public_profile' ), 'members', $member['language'], true ); $blurb = ''; $subject = ''; /* User made a reply to their own status */ if ( $_replier['member_id'] == $_owner['member_id'] ) { $blurb = $this->lang->words['email_blurb_theirs']; $subject = sprintf( $this->lang->words['email_title_blurb_other'], $_owner['members_display_name']); } else { $blurb = sprintf( $this->lang->words['email_blurb_other'], $_owner['members_display_name']); $subject = sprintf( $this->lang->words['email_title_blurb_other'], $_owner['members_display_name']); } $data = array( 'NAME' => $member['members_display_name'], 'OWNER' => $_owner['members_display_name'], 'POSTER' => $_replier['members_display_name'], 'BLURB' => $blurb, 'STATUS' => $status['status_content'], 'URL' => $this->registry->output->buildSEOUrl( 'app=core&module=usercp&tab=core&area=notifications', 'publicNoSession' ), 'TEXT' => $reply['reply_content'] ); IPSText::getTextClass('email')->getTemplate( 'status_reply', $member['language'] ); IPSText::getTextClass('email')->buildMessage( $data ); if ( $_replier['member_id'] == $_owner['member_id'] ) { IPSText::getTextClass('email')->subject = sprintf( $this->lang->words['subject__their_status_reply'], $this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ), $_replier['members_display_name'], $statusUrl ); } else { IPSText::getTextClass('email')->subject = sprintf( $this->lang->words['subject__other_status_reply'], $this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ), $_replier['members_display_name'], $this->registry->output->buildSEOUrl( 'showuser=' . $_owner['member_id'], 'public', $_owner['members_seo_name'], 'showuser' ), $_owner['members_display_name'], $statusUrl ); } $notifyLibrary->setMember( $member ); $notifyLibrary->setFrom( $_replier ); $notifyLibrary->setNotificationKey( 'reply_any_status' ); $notifyLibrary->setNotificationUrl( $statusUrl ); $notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message ); $notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject ); try { $notifyLibrary->sendNotification(); } catch( Exception $e ){} } } return TRUE; } } return FALSE; } /** * Send comment notification * * Emails users of new status replies * Emails owner (if selected) and anyone else who replied (if selected) * * @param array Author array * @param array Status array * @param array Reply array * @return boolean */ protected function _sendCommentNotification( $author, $owner, $status ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' ); $notifyLibrary = new $classToLoad( $this->registry ); $memberUrl = $this->registry->output->buildSEOUrl( 'showuser=' . $owner['member_id'] . '&tab=status', 'public', $owner['members_seo_name'], 'showuser' ); IPSText::getTextClass('email')->getTemplate( 'new_comment_added', $owner['language'] ); IPSText::getTextClass('email')->buildMessage( array( 'MEMBERS_DISPLAY_NAME' => $owner['members_display_name'], 'COMMENT_NAME' => $author['members_display_name'], 'LINK' => $memberUrl ) ); IPSText::getTextClass('email')->subject = sprintf( IPSText::getTextClass('email')->subject, $memberUrl, $this->registry->output->buildSEOUrl( 'showuser=' . $author['member_id'], 'public', $author['members_seo_name'], 'showuser' ), $author['members_display_name'] ); $notifyLibrary->setMember( $owner ); $notifyLibrary->setFrom( $author ); $notifyLibrary->setNotificationKey( 'profile_comment' ); $notifyLibrary->setNotificationUrl( $memberUrl ); $notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message ); $notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject ); try { $notifyLibrary->sendNotification(); } catch( Exception $e ){} return FALSE; } /** * Lock status * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return boolean */ public function lockStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( $status['status_id'] ) { if ( $this->canLockStatus( $author, $status ) ) { /* Value? */ $val = ( $author['g_is_supmod'] AND $author['member_id'] != $status['status_member_id'] ) ? 2 : 1; /* Update status */ $this->DB->update( 'member_status_updates', array( 'status_is_locked' => $val ), 'status_id=' . $status['status_id'] ); /* Rebuild latest */ $this->rebuildOwnerLatest( $author ); return TRUE; } } return FALSE; } /** * Unlock status * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return boolean */ public function unlockStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( $status['status_id'] ) { if ( $this->canUnlockStatus( $author, $status ) ) { /* Update status */ $this->DB->update( 'member_status_updates', array( 'status_is_locked' => 0 ), 'status_id=' . $status['status_id'] ); /* Rebuild latest */ $this->rebuildOwnerLatest( $author ); return TRUE; } } return FALSE; } /** * Approve status * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return boolean */ public function approveStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( $status['status_id'] ) { if ( $this->canApproveStatus( $author, $status ) ) { /* Update status */ $this->DB->update( 'member_status_updates', array( 'status_approved' => 1 ), 'status_id=' . $status['status_id'] ); /* Rebuild latest */ $this->rebuildOwnerLatest( $author ); return TRUE; } } return FALSE; } /** * Delete all user's status' * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @return boolean */ public function deleteAllMemberStatus( $author=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $statuses = array(); $this->DB->build( array( 'select' => '*', 'from' => 'member_status_updates', 'where' => 'status_member_id=' . $author['member_id'] ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $statuses[] = $row['status_id']; } /* Delete data */ if ( count( $statuses ) ) { $id = implode( ',', $statuses ); /* Delete replies */ $this->DB->delete( 'member_status_replies', 'reply_status_id IN(' . $id . ')' ); /* Delete actions */ $this->DB->delete( 'member_status_actions', 'action_status_id IN(' . $id . ')' ); /* Delete statuses */ $this->DB->delete( 'member_status_updates', 'status_id IN(' . $id . ')' ); } /* Flush caches */ $this->rebuildOwnerLatest( $author ); } /** * Delete all user's replies * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @return boolean */ public function deleteAllReplies( $author=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $statusIds = array(); $replyIds = array(); $memberIds = array(); $stom = array(); /* Fetch reply IDs */ $this->DB->build( array( 'select' => '*', 'from' => 'member_status_replies', 'where' => 'reply_member_id=' . $author['member_id'] ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $replyIds[] = $row['reply_id']; $statusIds[] = $row['reply_status_id']; } /* If nothing, goowf */ if ( ! count( $statusIds ) OR ! count( $replyIds ) ) { return FALSE; } /* Now fetch status owners to recache them */ $this->DB->build( array( 'select' => 'status_id, status_member_id', 'from' => 'member_status_updates', 'where' => 'status_id IN(' . implode( ',', $statusIds ) . ')' ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $memberIds[] = $row['status_member_id']; $stom[ $row['status_id'] ] = $row['status_member_id']; } /* Got member ids? */ $members = IPSMember::load( $memberIds, 'all' ); /* Delete data */ $id = implode( ',', $replyIds ); /* Delete replies */ $this->DB->delete( 'member_status_replies', 'reply_id IN(' . $id . ')' ); /* Delete actions */ $this->DB->delete( 'member_status_actions', 'action_reply_id IN(' . $id . ')' ); foreach( $statusIds as $sid ) { /* Rebuild this status */ $this->rebuildStatus( $sid ); /* Rebuild latest */ $this->rebuildOwnerLatest( $members[ $stom[ $sid ] ] ); } } /** * Delete status * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return boolean */ public function deleteStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( $status['status_id'] ) { if ( $this->canDeleteStatus( $author, $status ) ) { /* Delete status */ $this->DB->delete( 'member_status_updates', 'status_id=' . $status['status_id'] ); /* Delete replies */ $this->DB->delete( 'member_status_replies', 'reply_status_id=' . $status['status_id'] ); /* Delete actions */ $this->DB->delete( 'member_status_actions', 'action_status_id=' . $status['status_id'] ); /* Rebuild latest */ $this->rebuildOwnerLatest( $author ); return TRUE; } } return FALSE; } /** * Delete reply * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @param array [Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none] * @return boolean */ public function deleteReply( $author=null, $status=null, $reply=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); $reply = ( $reply === null ) ? $this->_internalData['ReplyData'] : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) ); if ( $status['status_id'] AND $reply['reply_id'] ) { if ( $this->canDeleteReply( $author, $reply, $status ) ) { /* Delete replies */ $this->DB->delete( 'member_status_replies', 'reply_id=' . $reply['reply_id'] ); /* Delete actions */ $this->DB->delete( 'member_status_actions', 'action_reply_id=' . $reply['reply_id'] ); /* Rebuild this status */ $this->rebuildStatus( $status ); /* Rebuild latest */ $this->rebuildOwnerLatest( $author ); return TRUE; } } return FALSE; } /** * Rebuild status data * * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return boolean */ public function rebuildStatus( $status=null ) { $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); $last = array(); if ( $status['status_id'] ) { /* Fetch the number of replies */ $count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as count', 'from' => 'member_status_replies', 'where' => 'reply_status_id=' . intval( $status['status_id'] ) ) ); /* Fetch last 3 replies */ $this->DB->build( array( 'select' => 'reply_id, reply_member_id, reply_date, reply_content', 'from' => 'member_status_replies', 'order' => 'reply_date DESC', 'limit' => array( 0, 3 ), 'where' => 'reply_status_id=' . intval( $status['status_id'] ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $last[ $row['reply_date'] ] = $row; } if ( count( $last ) ) { ksort( $last ); } /* Reset data */ $this->DB->update( 'member_status_updates', array( 'status_replies' => intval( $count['count'] ), 'status_last_ids' => serialize( $last ) ), 'status_id=' . $status['status_id'] ); } return TRUE; } /** * Rebuilds status data such as last status update, etc. * * @param array [Array of member data for member updating their status - will use ->getAuthor() if null] * @return boolean */ public function rebuildOwnerLatest( $author=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $last = array(); /* Reset last flag for everyone */ $this->DB->update( 'member_status_updates', array( 'status_is_latest' => 0 ), 'status_member_id=' . intval( $author['member_id'] ) ); /* Fetch the latest update */ $status = $this->fetchMemberLatest( $author['member_id'] ); if ( $status['status_id'] ) { /* Fetch the number of replies */ $count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as count', 'from' => 'member_status_replies', 'where' => 'reply_status_id=' . intval( $status['status_id'] ) ) ); /* Fetch last 3 replies */ $this->DB->build( array( 'select' => 'reply_id, reply_member_id, reply_date, reply_content', 'from' => 'member_status_replies', 'order' => 'reply_date DESC', 'limit' => array( 0, 3 ), 'where' => 'reply_status_id=' . intval( $status['status_id'] ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $last[ $row['reply_date'] ] = $row; } ksort( $last ); /* Reset data */ $this->DB->update( 'member_status_updates', array( 'status_is_latest' => 1, 'status_replies' => intval( $count['count'] ), 'status_last_ids' => serialize( $last ) ), 'status_id=' . $status['status_id'] ); } return TRUE; } /** * Fetch the status update by hash * * @param int Member ID */ public function fetchByHash( $memberId, $hash ) { $status = array(); $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_updates' => 's' ), 'where' => 's.status_member_id=' . intval( $memberId ) . ' AND s.status_imported=1 AND s.status_hash=\'' . addslashes( $hash ) . '\'', 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.status_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $this->DB->execute(); $status = $this->DB->fetch(); return is_array( $status ) ? $status : array(); } /** * Fetch the member's latest status update * * @param int Member ID */ public function fetchMemberLatest( $memberId ) { $status = array(); $member = array(); $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_updates' => 's' ), 'where' => 's.status_member_id=' . intval( $memberId ) . ' AND s.status_author_id=' . intval( $memberId ), 'order' => 's.status_date DESC', 'limit' => array( 0, 1 ), 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.status_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $this->DB->execute(); $row = $this->DB->fetch(); if ( is_array( $row ) AND count( $row ) ) { foreach( $row as $k => $v ) { if ( substr( $k, 0, 7 ) != 'status_' ) { $member[ $k ] = $v; } $status[ $k ] = $v; } } $this->_internalData['StatusData'] = $status; $this->_internalData['StatusOwner'] = $member; return is_array( $status ) ? $status : array(); } /** * Fetch creator image * * @return string (img URL) */ public function creatorImage( $status ) { /* Got a creator? */ if ( ! $status['status_creator'] ) { $status['status_creator'] = 'ipb'; } $creator = IPSText::alphanumericalClean( $status['status_creator'] ); /* Image exists? */ if ( ! is_file( IPS_PUBLIC_PATH . 'style_status/' . $creator . '.png' ) ) { $creator = 'ipb'; } return $this->settings['public_dir'] . 'style_status/' . $creator . '.png'; } /** * Fetch creator text * * @return string */ public function creatorText( $status ) { /* Got a creator? */ if ( ! $status['status_creator'] ) { $status['status_creator'] = 'ipb'; } if ( ! isset( $this->lang->words['status_creator_' . $status['status_creator'] ] ) ) { $status['status_creator'] = 'ipb'; } return $this->lang->words['status_creator_' . $status['status_creator'] ]; } /** * Perm Check: Can post a status update * * @return bool */ public function canCreate( $author=null, $owner=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; if ( $author['restrict_post'] ) { return FALSE; } if ( $owner !== null AND $author['member_id'] != $owner['member_id'] ) { /* Not a new status by user */ return $this->canComment( $author, $owner ); } if ( ! $author['member_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_mem_info'] OR $author['gbw_no_status_update'] OR $author['bw_no_status_update'] ) { return FALSE; } return TRUE; } /** * Perm Check: Can post a comment * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status owner information uses $this->_internalData['StatusOwner'] if none] * @return bool */ public function canComment( $author=null, $owner=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $owner = ( $owner === null ) ? $this->_internalData['StatusOwner'] : $owner; /* Are we allowed to comment? */ if ( ! $author['g_reply_other_topics'] ) { return false; } if ( $author['restrict_post'] ) { if ( $author['restrict_post'] == 1 ) { return false; } $post_arr = IPSMember::processBanEntry( $author['restrict_post'] ); if ( time() >= $post_arr['date_end'] ) { /* Update this member's profile */ IPSMember::save( $author['member_id'], array( 'core' => array( 'restrict_post' => 0 ) ) ); } else { return false; } } if ( IPSMember::checkIgnoredStatus( $author['member_id'], $owner['member_id'], 'messages' ) ) { return false; } return true; } /** * Perm Check: Can post a status update * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status owner information uses $this->_internalData['StatusOwner'] if none] * @param array [Array of status data information uses $this->_internalData['StatusData'] if none] * @return bool */ public function canReply( $author=null, $owner=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $owner = ( $owner === null ) ? $this->_internalData['StatusOwner'] : $owner; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_mem_info'] OR $author['gbw_no_status_update'] OR $author['bw_no_status_update'] ) { return FALSE; } if ( ! $this->_okToPost( $author, $owner ) ) { return FALSE; } if ( $status['status_replies'] >= $this->settings['su_max_replies'] ) { return FALSE; } if ( $status['status_is_locked'] ) { if ( ! $author['g_is_supmod'] ) { if ( $status['status_is_locked'] == 1 AND ( $author['member_id'] == $status['status_member_id'] ) ) { return TRUE; } else { return FALSE; } } } return TRUE; } /** * Perm Check: Can delete a status update * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function canDeleteStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] OR ! $status['status_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } /* is left by someone else */ if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_author_id'] == $author['member_id'] ) { return true; } if ( ! $author['g_is_supmod'] ) { if ( $author['gbw_no_status_update'] OR $author['bw_no_status_update'] ) { return FALSE; } if ( $author['member_id'] != $status['status_member_id'] ) { return FALSE; } } return TRUE; } /** * Perm Check: Can delete a reply * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function canDeleteReply( $author=null, $reply=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $reply = ( $reply === null ) ? $this->_internalData['ReplyData'] : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) ); $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] OR ! $status['status_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_is_supmod'] ) { if ( $author['gbw_no_status_update'] OR $author['bw_no_status_update'] ) { return FALSE; } if ( $author['member_id'] == $status['status_member_id'] ) { return TRUE; } if ( $author['member_id'] == $reply['reply_member_id'] ) { return TRUE; } return FALSE; } return TRUE; } /** * Perm Check: Can approve a status * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function canApproveStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] OR ! $status['status_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_is_supmod'] ) { if ( $author['member_id'] == $status['status_member_id'] && !IPSMember::isOnModQueue( $author['member_id'] )) { return TRUE; } return FALSE; } else { return true; } /* is left by someone else */ if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_member_id'] != $author['member_id'] ) { return false; } return TRUE; } /** * Perm Check: Can lock a status * status_is_locked: 1 means user lock, so owner can unlock, 2 means admin lock, so cannot unlock. * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function canLockStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] OR ! $status['status_id'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_is_supmod'] ) { if ( $author['member_id'] == $status['status_member_id'] ) { return TRUE; } return FALSE; } else { return true; } /* is left by someone else */ if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_member_id'] != $author['member_id'] ) { return false; } return TRUE; } /** * Perm Check: Can lock a status * status_is_locked: 1 means user lock, so owner can unlock, 2 means admin lock, so cannot unlock. * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @return bool */ public function canUnlockStatus( $author=null, $status=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) ); if ( ! $author['member_id'] OR ! $status['status_id'] OR ! $status['status_is_locked'] ) { return FALSE; } if ( ! $this->isEnabled() ) { return FALSE; } if ( ! $author['g_is_supmod'] ) { if ( $author['member_id'] == $status['status_member_id'] AND ( $status['status_is_locked'] == 1 ) ) { return TRUE; } return FALSE; } return TRUE; } /** * Record a custom action * * @param string Type (new or reply) * @param array text, url, app * @param array [Array of author data, uses getAuthor if none] */ public function recordAction( $type, $custom=array(), $author=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; if ( $author['member_id'] AND $custom['text'] ) { $this->DB->insert( 'member_status_actions', array( 'action_member_id' => $author['member_id'], 'action_date' => time(), 'action_custom' => 1, 'action_custom_text' => $custom['text'], 'action_custom_url' => $custom['url'], 'action_app' => $custom['app'], 'action_key' => 'custom' ) ); } } /** * Record an action * * @param string Type (new or reply) * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none] * @param array [Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none] */ protected function _recordAction( $type, $author=null, $status=null, $reply=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $status = ( $status === null ) ? $this->_internalData['StatusData'] : ( is_array( $status ) ) ? $status : ( $status ? $this->_loadStatus( $status ) : array() ); $reply = ( $reply === null ) ? $this->_internalData['ReplyData'] : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) ); if ( $author['member_id'] AND $status['status_id'] ) { $this->DB->insert( 'member_status_actions', array( 'action_status_id' => $status['status_id'], 'action_reply_id' => intval( $reply['reply_id'] ), 'action_member_id' => $author['member_id'], 'action_status_owner' => $status['status_member_id'], 'action_date' => time(), 'action_custom' => 0, 'action_key' => ( $type == 'new' ) ? 'new' : 'reply' ) ); } } /** * Load a status from the DB * * @param int Status ID * @return array Array OR FALSE */ protected function _loadStatus( $statusId ) { $status = array(); $member = array(); $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_updates' => 's' ), 'where' => 's.status_id=' . intval( $statusId ), 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.status_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $this->DB->execute(); $row = $this->DB->fetch(); if( ! $row ) { return FALSE; } foreach( $row as $k => $v ) { if ( substr( $k, 0, 7 ) != 'status_' ) { $member[ $k ] = $v; } $status[ $k ] = $v; } $this->_internalData['StatusData'] = $status; $this->_internalData['StatusOwner'] = $member; return ( isset( $status['status_id'] ) ) ? $status : FALSE; } /** * Load a reply from the DB * * @param int Status ID * @return array Array OR FALSE */ protected function _loadReply( $replyId ) { $reply = array(); $member = array(); $this->DB->build( array( 'select' => 's.*', 'from' => array( 'member_status_replies' => 's' ), 'where' => 's.reply_id=' . intval( $replyId ), 'add_join' => array(array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=s.reply_member_id', 'type' => 'left' ), array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ) ) ) ); $this->DB->execute(); $row = $this->DB->fetch(); if( ! $row ) { return FALSE; } foreach( $row as $k => $v ) { if ( substr( $k, 0, 6 ) != 'reply_' ) { $member[ $k ] = $v; } $reply[ $k ] = $v; } $this->_internalData['ReplyData'] = $reply; $this->_internalData['ReplyOwner'] = $member; return ( isset( $reply['reply_id'] ) ) ? $reply : FALSE; } /** * Clean up content suitable for posting * * @param string In * @return string Out */ protected function _cleanContent( $content ) { /* Just cut off after X chars. */ $content = IPSText::truncate( IPSText::getTextClass('bbcode')->stripBadWords( $content ), $this->settings['su_max_chars'] ); return $content; } /** * Auto parse some stuff * * Eventually could abstract it out but for now, this will do. Mkay. */ protected function _parseContent( $content, $creator='' ) { /* Auto parse tags */ if ( $this->settings['su_parse_url'] ) { $content = preg_replace_callback( '#(^|\s|\(|>|\](?settings['tc_parse_tags'] ) { $content = preg_replace_callback( '#(^|\s)(\\#([a-z_A-Z0-9:_-]+))#', array( $this, '_autoParseTags' ), $content ); } if ( $this->settings['tc_parse_names'] ) { $content = preg_replace_callback('#(^|\s)@([a-z_A-Z0-9]+)#', array( $this, '_autoParseNames' ), $content ); } } return $content; } /** * Callback to auto-parse @names * * @param array Matches from the regular expression * @return string Converted text */ protected function _autoParseNames( $matches ) { return $this->_autoParseUrls( array( '', $matches[1], 'http://www.twitter.com/' . urlencode( $matches[2] ), '@' . $matches[2] ) ); } /** * Callback to auto-parse #tags * * @param array Matches from the regular expression * @return string Converted text */ protected function _autoParseTags( $matches ) { return $this->_autoParseUrls( array( '', $matches[1], 'http://search.twitter.com/search?q=%23' . urlencode( $matches[3] ), $matches[2] ) ); } /** * Callback to auto-parse urls * I totally stole this from Brandon's code but do NOT tell him. * * @param array Matches from the regular expression * @return string Converted text */ protected function _autoParseUrls( $matches ) { /* We use this function in other areas also */ $matches[3] = ( $matches[3] ) ? $matches[3] : $matches[2]; //----------------------------------------- // Adding rel='nofollow'? //----------------------------------------- $rels = array(); $rel = ''; if( $this->settings['posts_add_nofollow'] ) { $rels[] = "nofollow"; } if( $this->settings['links_external'] ) { $rels[] = "external"; } if( count($rels) ) { $rel = " rel='" . implode( ' ', $rels ) . "'"; } return $matches[1] . '' . $matches[3] . ''; } /** * Check for mod posts or restricted posts or ignored * * @param array [Array of author data, uses getAuthor if none] * @param array [Array of status owner information uses $this->_internalData['StatusOwner'] if none] * @return bool */ protected function _okToPost( $author=null, $owner=null ) { $author = ( $author === null ) ? $this->getAuthor() : $author; $owner = ( $owner === null ) ? $this->_internalData['StatusOwner'] : $owner; /* Restricted Posting */ if ( $author['restrict_post'] ) { if ( $author['restrict_post'] == 1 ) { return FALSE; } $post_arr = IPSMember::processBanEntry( $author['restrict_post'] ); if( time() >= $post_arr['date_end'] ) { /* Update this member's profile */ IPSMember::save( $author['member_id'], array( 'core' => array( 'restrict_post' => 0 ) ) ); } else { return FALSE; } } /* Moderated Posting */ if ( $author['mod_posts'] ) { if ( $author['mod_posts'] == 1 ) { return FALSE; } else { $mod_arr = IPSMember::processBanEntry( $author['mod_posts'] ); if( time() >= $mod_arr['date_end'] ) { /* Update this member's profile */ IPSMember::save( $author['member_id'], array( 'core' => array( 'mod_posts' => 0 ) ) ); } else { return FALSE; } } } /* Member is ignoring you! */ if ( IPSMember::checkIgnoredStatus( $author['member_id'], $owner['member_id'], 'messages' ) ) { return false; } return TRUE; } /** * Various getters and setters * * @param string * @param mixed void, or an array of arguments * @return mixed Data */ public function __call( $method, $arguments ) { $firstBit = substr( $method, 0, 3 ); $theRest = substr( $method, 3 ); if ( in_array( $theRest, $this->_allowedInternalData ) ) { if ( $firstBit == 'set' ) { if ( $theRest == 'Author' OR $theRest == 'Friend' ) { if ( is_array( $arguments[0] ) ) { /* Ensure we have group data */ if ( ! isset( $arguments[0]['g_id'] ) AND $arguments[0]['member_id'] ) { $this->_internalData[ $theRest ] = IPSMember::load( intval( $arguments[0]['member_id'] ), 'all' ); } else { $this->_internalData[ $theRest ] = $arguments[0]; } } else { if( $arguments[0] ) { /* Set up moderator stuff, too */ $this->_internalData[ $theRest ] = IPSMember::load( intval( $arguments[0] ), 'all' ); } else { $this->_internalData[ $theRest ] = IPSMember::setUpGuest(); } } } else if ( $theRest == 'StatusData' ) { if ( is_array( $arguments[0] ) ) { $this->_internalData[ $theRest ] = $arguments[0]; } else { $this->_internalData[ $theRest ] = $this->_loadStatus( intval( $arguments[0] ) ); } } else if ( $theRest == 'ReplyData' ) { if ( is_array( $arguments[0] ) ) { $this->_internalData[ $theRest ] = $arguments[0]; } else { $this->_internalData[ $theRest ] = $this->_loadReply( intval( $arguments[0] ) ); } } else { $this->_internalData[ $theRest ] = $arguments[0]; return TRUE; } return $this->_internalData[ $theRest ]; } else { if ( ( $theRest == 'Author' OR $theRest == 'Friend' OR $theRest == 'StatusData' OR $theRest == 'ReplyData' ) AND isset( $arguments[0] ) ) { return isset( $this->_internalData[ $theRest ][ $arguments[0] ] ) ? $this->_internalData[ $theRest ][ $arguments[0] ] : ''; } else { return isset( $this->_internalData[ $theRest ] ) ? $this->_internalData[ $theRest ] : ''; } } } } }