Вход Регистрация
Файл: library/XenForo/Model/NewsFeed.php
Строк: 733
<?php

/**
 * Model class for manipulating the news feed.
 *
 * @author kier
 */
class XenForo_Model_NewsFeed extends XenForo_Model
{
    
/**
     * Array to store news feed handler classes
     *
     * @var array
     */
    
protected $_handlerCache = array();

    
/**
     * Fetches a single news feed item using its ID
     *
     * @param integer $newsFeedId
     *
     * @return array|false
     */
    
public function getNewsFeedItemById($newsFeedId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT *
            FROM xf_news_feed
            WHERE news_feed_id = ?
        '
$newsFeedId);
    }

    
/**
     * Returns news feed data for the specified user.
     * By default, returns the most recent items unless a 'fetchOlderThanId' is specified.
     *
     * @param array $user
     * @param integer If specified, switches the mode of the function to return results *older* than the specified news feed id
     * @param array|null $viewingUser Information about the viewing user (keys: user_id, permission_combination_id, permissions) or null for visitor
     *
     * @return array
     */
    
public function getNewsFeedForUser(array $user$fetchOlderThanId 0, array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (
$fetchOlderThanId)
        {
            
$newsFeed $this->getNewsFeedItemsForUser($user,
                array(
'news_feed_id' => array('<'$fetchOlderThanId))
            );
        }
        else
        {
            
$cacheNewsFeed $this->getNewsFeedCache($user['user_id']);
            
$newestItemId $this->getNewestNewsFeedIdFromArray($cacheNewsFeed);

            
$newsFeed $this->getNewsFeedItemsForUser($user,
                array(
'news_feed_id' => array('>'$newestItemId))
            );
        }

        
$newsFeed $this->fillOutNewsFeedItems($newsFeed$viewingUser);

        if (!
$fetchOlderThanId)
        {
            
$updateCache = (count($newsFeed) > 0);

            
$newsFeed $this->_mergeLatestNewsFeedItemsWithCache($newsFeed$cacheNewsFeed);
            if (
$updateCache AND $user['user_id'] == $viewingUser['user_id'])
            {
                
$this->_saveCache($user['user_id'], $newsFeed);
            }
        }

        
$this->_cacheHandlersForNewsFeed($newsFeed);

        return array(
            
'newsFeed' => $newsFeed,
            
'newsFeedHandlers' => $this->_handlerCache,
            
'oldestItemId' => $this->getOldestNewsFeedIdFromArray($newsFeed),
            
'feedEnds' => (sizeof($newsFeed) == 0// permissions make this hard to calculate
        
);
    }

    
/**
     * Gets a news feed with the specified conditions.
     *
     * @param array $conditions
     * @param integer $fetchOlderThanId If > 0, only fetches items with a lower ID than this
     * @param array|null $viewingUser
     *
     * @return array
     */
    
public function getNewsFeed(array $conditions = array(), $fetchOlderThanId 0, array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (
$fetchOlderThanId)
        {
            
$conditions['news_feed_id'] = array('<'$fetchOlderThanId);
        }

        
$newsFeed $this->getNewsFeedItems($conditions$viewingUser);

        
$newsFeed $this->fillOutNewsFeedItems($newsFeed$viewingUser);
        
$this->_cacheHandlersForNewsFeed($newsFeed);

        return array(
            
'newsFeed' => $newsFeed,
            
'newsFeedHandlers' => $this->_handlerCache,
            
'oldestItemId' => $this->getOldestNewsFeedIdFromArray($newsFeed),
            
'feedEnds' => (sizeof($newsFeed) == 0// permissions make this hard to calculate
        
);
    }

    
/**
     * Fills out a collection of news feed items, to include the necessary content and prepares
     * them for view. Also filters out unviewable items.
     *
     * @param array $newsFeed
     * @param array $viewingUser
     *
     * @return array
     */
    
public function fillOutNewsFeedItems(array $newsFeed, array $viewingUser)
    {
        if (
$newsFeed)
        {
            
$newsFeed $this->_getContentForNewsFeedItems($newsFeed$viewingUser);
            
$newsFeed $this->_getViewableNewsFeedItems($newsFeed$viewingUser);
            
$newsFeed $this->_prepareNewsFeedItems($newsFeed$viewingUser);
        }

        return 
$newsFeed;
    }

    
/**
     * Caches an instance of every news feed handler required by the data provided
     *
     * @param array $newsFeed
     */
    
protected function _cacheHandlersForNewsFeed(array $newsFeed)
    {
        foreach (
$newsFeed AS $item)
        {
            
$this->_getNewsFeedHandlerFromCache($item['content_type']);
        }
    }

    
/**
     * Gets news feed items for a particular viewing user. This will get news feed items
     * for all user he/she follows.
     *
     * @param array $viewingUser
     * @param array $conditions
     * @param integer|null $maxItems
     *
     * @return array
     */
    
public function getNewsFeedItemsForUser(array $viewingUser, array $conditions = array(), $maxItems null)
    {
        
$followingIds $this->_getDb()->fetchCol('
            SELECT follow_user_id
            FROM xf_user_follow
            WHERE xf_user_follow.user_id = ?
        '
$viewingUser['user_id']);

        if (!
$followingIds)
        {
            return array();
        }

        
$conditions['user_id'] = $followingIds;

        return 
$this->getNewsFeedItems($conditions$viewingUser$maxItems);
    }

    
/**
     * Gets news feed items matching the given conditions.
     *
     * @param array $conditions
     * @param array $viewingUser
     * @param integer|null $maxItems
     *
     * @return array
     */
    
public function getNewsFeedItems(array $conditions = array(), array $viewingUser$maxItems null)
    {
        
$db $this->_getDb();
        
$sqlConditions = array();

        if (isset(
$conditions['news_feed_id']) && is_array($conditions['news_feed_id']))
        {
            list(
$operator$newsFeedId) = $conditions['news_feed_id'];

            
$this->assertValidCutOffOperator($operator);
            
$sqlConditions[] = "news_feed.news_feed_id $operator " $db->quote($newsFeedId);
        }

        if (isset(
$conditions['user_id']))
        {
            if (
is_array($conditions['user_id']))
            {
                
$sqlConditions[] = 'news_feed.user_id IN (' $db->quote($conditions['user_id']) . ')';
            }
            else
            {
                
$sqlConditions[] = 'news_feed.user_id = ' $db->quote($conditions['user_id']);
            }
            
$forceIndex '';
        }
        else
        {
            
$forceIndex 'FORCE INDEX (event_date)';

            if (
$viewingUser['user_id'] && !empty($viewingUser['ignored']))
            {
                
$ignored unserialize($viewingUser['ignored']);
                if (
$ignored)
                {
                    
$ignored array_map('intval'array_keys($ignored));
                    
$sqlConditions[] = 'news_feed.user_id NOT IN (' $db->quote($ignored) . ')';
                }
            }

            
$sqlConditions[] = "user.user_state IN ('valid', 'email_confirm_edit')";
            
$sqlConditions[] = "user.is_banned = 0";
        }

        
$whereClause $this->getConditionsForClause($sqlConditions);

        if (
$maxItems === null)
        {
            
$maxItems XenForo_Application::get('options')->newsFeedMaxItems;
        }

        
$viewingUserIdQuoted $db->quote($viewingUser['user_id']);
        
$isRegistered = ($viewingUser['user_id'] > 0);
        
$bypassPrivacy $this->getModelFromCache('XenForo_Model_User')->canBypassUserPrivacy($errorPhraseKey$viewingUser);

        
// TODO: restore user_id = 0 announcements functionality down the line
        
return $this->fetchAllKeyed($this->limitQueryResults(
            
'
                SELECT
                    user.*,
                    user_profile.*,
                    user_privacy.*,
                    news_feed.*
                FROM xf_news_feed AS news_feed ' 
$forceIndex '
                INNER JOIN xf_user AS user ON
                    (user.user_id = news_feed.user_id)
                INNER JOIN xf_user_profile AS user_profile ON
                    (user_profile.user_id = user.user_id)
                LEFT JOIN xf_user_follow AS user_follow ON
                    (user_follow.user_id = user.user_id
                    AND user_follow.follow_user_id = ' 
$viewingUserIdQuoted ')
                INNER JOIN xf_user_privacy AS user_privacy ON
                    (user_privacy.user_id = user.user_id
                        ' 
. ($bypassPrivacy '' '
                            AND (user.user_id = ' 
$viewingUserIdQuoted '
                                OR (
                                    user_privacy.allow_receive_news_feed <> '
none'
                                    AND IF(user_privacy.allow_receive_news_feed = '
members', ' $isRegistered ', 1)
                                    AND IF(user_privacy.allow_receive_news_feed = '
followed', user_follow.user_id IS NOT NULL, 1)
                                )
                            )
                        '
) . '
                    )
                WHERE ' 
$whereClause '
                ORDER BY news_feed.event_date DESC
            '
$maxItems
        
), 'news_feed_id');
    }

    
/**
     * Gets the ID of the newest feed item in an array of feed items
     *
     * @param array News feed array
     *
     * @return integer
     */
    
public function getNewestNewsFeedIdFromArray(array $newsFeed)
    {
        if (empty(
$newsFeed))
        {
            return 
0;
        }

        return 
max(array_keys($newsFeed));
    }

    
/**
     * Gets the ID of the oldest feed item in an array of news feed items
     *
     * @param array News feed array
     *
     * @return integer
     */
    
public function getOldestNewsFeedIdFromArray(array $newsFeed)
    {
        if (empty(
$newsFeed))
        {
            return 
0;
        }

        return 
min(array_keys($newsFeed));
    }

    
/**
     * Fetches content data for news feed items
     *
     * @param array $data Raw news feed data
     * @param array $viewingUser Information about the viewing user (keys: user_id, permission_combination_id, permissions)
     *
     * @return array
     */
    
protected function _getContentForNewsFeedItems(array $data, array $viewingUser)
    {
        
// group all content ids of each content type...
        
$fetchQueue = array();
        foreach (
$data AS $id => $item)
        {
            
$fetchQueue[$item['content_type']][$item['news_feed_id']] = $item['content_id'];
        }

        
// fetch data for all items of each content type in one go...
        
$fetchData = array();
        foreach (
$fetchQueue AS $contentType => $contentIds)
        {
            
$handler $this->_getNewsFeedHandlerFromCache($contentType);
            if (!
$handler)
            {
                continue;
            }

            
$fetchData[$contentType] = $handler->getContentByIds($contentIds$this$viewingUser);
        }

        
// attach resulting content to each feed item...
        
foreach ($data AS $id => $item)
        {
            if (!isset(
$fetchData[$item['content_type']][$item['content_id']]))
            {
                
// For whatever reason, there was no related content found for this news feed item,
                // therefore remove it from this user's news feed
                
unset($data[$id]);
                continue;
            }

            
$data[$id]['content'] = $fetchData[$item['content_type']][$item['content_id']];
        }

        return 
$data;
    }

    
/**
     * Filters out unviewable news feed items and returns only those the user can view.
     *
     * @param array $items
     * @param array $viewingUser
     *
     * @return array Filtered items
     */
    
protected function _getViewableNewsFeedItems(array $items, array $viewingUser)
    {
        foreach (
$items AS $key => $item)
        {
            
$handler $this->_getNewsFeedHandlerFromCache($item['content_type']);
            if (!
$handler || !$handler->canViewNewsFeedItem($item$item['content'], $viewingUser))
            {
                unset(
$items[$key]);
            }
        }

        return 
$items;
    }

    
/**
     * Takes all new feed items and appends old items from the cache until the array
     * contains options->newsFeedMaxItems items
     *
     * @param array $newsFeed
     * @param array $cachedItems
     */
    
protected function _mergeLatestNewsFeedItemsWithCache(array $newsFeed, array $cachedItems)
    {
        
$i sizeof($newsFeed);
        
$maxFeedItems XenForo_Application::get('options')->newsFeedMaxItems;

        while (
$i++ < $maxFeedItems && list($id$item) = each($cachedItems))
        {
            
$newsFeed[$item['news_feed_id']] = $item;
        }

        return 
$newsFeed;
    }

    
/**
     * Runs prepareNewsFeedItem on an array of items
     *
     * @param array $items
     * @param array $viewingUser Information about the viewing user (keys: user_id, permission_combination_id, permissions)
     *
     * @return array
     */
    
protected function _prepareNewsFeedItems(array $items, array $viewingUser)
    {
        foreach (
$items AS $id => $item)
        {
            
$handlerClass $this->_getNewsFeedHandlerForContent($item['content_type']);
            
$items[$id] = $this->_prepareNewsFeedItem($item$handlerClass$viewingUser);
        }

        return 
$items;
    }

    
/**
     * Wraps around the prepareX functions in the handler class for each content type.
     * Also does basic setup such as fetching user avatars.
     *
     * @param array $newsFeedItem
     * @param string $handlerClassName
     * @param array $viewingUser Information about the viewing user (keys: user_id, permission_combination_id, permissions)
     *
     * @return mixed
     */
    
protected function _prepareNewsFeedItem(array $item$handlerClassName, array $viewingUser)
    {
        
$item['user'] = array(
            
'user_id' => $item['user_id'],
            
'username' => $item['username'],
        );

        
$handler $this->_getNewsFeedHandlerFromCache($item['content_type']);
        if (
$handler)
        {
            
$item $handler->prepareNewsFeedItem($item$viewingUser);
        }

        return 
$item;
    }

    
/**
     * Saves a user's news feed into their cache
     *
     * @param integer $userId
     * @param array $newsFeed
     *
     * @return integer news_feed_id of latest news feed item in the cache
     */
    
protected function _saveCache($userId, array $newsFeed)
    {
        
$latestNewsFeedId $this->getNewestNewsFeedIdFromArray($newsFeed);

        if (
XenForo_Application::get('options')->newsFeedCache)
        {
            
$this->_getDb()->query('

                INSERT INTO xf_user_news_feed_cache
                    (user_id, news_feed_cache, news_feed_cache_date)
                VALUES
                    (?, ?, ?)
                ON DUPLICATE KEY UPDATE
                    news_feed_cache = VALUES(news_feed_cache),
                    news_feed_cache_date = VALUES(news_feed_cache_date)

            '
, array($userIdserialize($newsFeed), XenForo_Application::$time
            
));
        }

        return 
$latestNewsFeedId;
    }

    
/**
     * Fetches a user's cached news feed
     *
     * @param integer $userId
     *
     * @return array
     */
    
public function getNewsFeedCache($userId)
    {
        
$options XenForo_Application::get('options');

        if (
$options->newsFeedCache)
        {
            
$newsFeed $this->_getDb()->fetchOne('

                SELECT news_feed_cache
                FROM xf_user_news_feed_cache
                WHERE user_id = ?

            '
$userId);

            if (
$newsFeed unserialize($newsFeed))
            {
                if (
sizeof($newsFeed) > $options->newsFeedMaxItems)
                {
                    return 
array_slice($newsFeed0$options->newsFeedMaxItemstrue);
                }

                return 
$newsFeed;
            }
        }

        return array();
    }

    
/**
     * Clears a user's cache, forcing a clean fetch on the next news feed load
     *
     * @param integer $userId
     */
    
public function deleteNewsFeedCache($userId)
    {
        
$db $this->_getDb();

        
$db->delete('xf_user_news_feed_cache''user_id = ' $db->quote($userId));
    }

    
/**
     * Publish a news feed item
     *
     * @param integer $userId
     * @param string $username
     * @param string $contentType
     * @param integer $contentId
     * @param string $action
     * @param array $extraData
     */
    
public function publish($userId$username$contentType$contentId$action, array $extraData null)
    {
        if (!
$userId)
        {
            return;
        }

        
$dw XenForo_DataWriter::create('XenForo_DataWriter_NewsFeed');

        
$dw->set('user_id'$userId);
        
$dw->set('username'$username);
        
$dw->set('content_type'$contentType);
        
$dw->set('content_id'$contentId);
        
$dw->set('action'$action);
        
$dw->set('extra_data'$extraData);

        
$dw->save();
    }

    
/**
     * Permanently delete an item or items from the news feed
     *
     * @param string $contentType
     * @param integer $contentId
     * @param integer $userId (optional)
     * @param string $action (optional)
     */
    
public function delete($contentType$contentId$userId null$action null)
    {
        
$db $this->_getDb();

        
$deleteCondition 'content_type = ' $db->quote($contentType) . ' AND content_id = ' $db->quote($contentId);

        if (isset(
$userId))
        {
            
$deleteCondition .= ' AND user_id = ' $db->quote($userId);

            if (isset(
$action))
            {
                
$deleteCondition .= ' AND action = ' $db->quote($action);
            }
        }

        return 
$db->delete('xf_news_feed'$deleteCondition);
    }

    
/**
     * Deletes old news feed items. This does not trigger a cache rebuild, so a user could keep old records around
     * until they're pushed off.
     *
     * @param integer|null $dateCut Uses default setting if null
     */
    
public function deleteOldNewsFeedItems($dateCut null)
    {
        if (
$dateCut === null)
        {
            
$expiryTime 86400// TODO: hard coded to 7 days
            
$dateCut XenForo_Application::$time $expiryTime;
        }

        
$db $this->_getDb();
        
$db->delete('xf_news_feed''event_date < '$db->quote($dateCut));
    }

    protected function 
_getNewsFeedHandlerForContent($contentType)
    {
        return 
$this->getContentTypeField($contentType'news_feed_handler_class');
    }

    
/**
     * Fetches an instance of the specified news feed handler class
     *
     * @param string $contentType
     *
     * @return XenForo_NewsFeedHandler_Abstract
     */
    
protected function _getNewsFeedHandlerFromCache($contentType)
    {
        
$class $this->_getNewsFeedHandlerForContent($contentType);
        if (!
$class || !class_exists($class))
        {
            return 
false;
        }

        if (!isset(
$this->_handlerCache[$contentType]))
        {
            
$this->_handlerCache[$contentType] = XenForo_NewsFeedHandler_Abstract::create($class);
        }

        return 
$this->_handlerCache[$contentType];
    }

    
/**
     * Fetches an instance of the user model
     *
     * @return XenForo_Model_User
     */
    
protected function _getUserModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_User');
    }
}
Онлайн: 1
Реклама