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

class XenForo_Model_Feed extends XenForo_Model
{
    
/**
     * Valid values (seconds) for feed.frequency
     *
     * @var array
     */
    
protected static $_frequencyValues = array(
        
600// 10 mins
        
1200// 20 mins
        
1800// 30 mins
        
3600// 1 hour
        
7200// 2 hours
        
14400// 4 hours
        
21600// 6 hours
        
43200// 12 hours
    
);

    
/**
     * Maximum number of entries to be fetched per-feed per-import.
     *
     * @var integer
     */
    
protected static $_maxEntriesPerImport 5;

    
/**
     * Fetch all info for a single feed record.
     *
     * @param integer $feedId
     *
     * @return array
     */
    
public function getFeedById($feedId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT feed.*, IF(feed.active AND node.node_id, 1, 0) AS active,
                node.title AS node_title, node.node_name,
                user.username
            FROM xf_feed AS feed
            LEFT JOIN xf_node AS node ON
                (node.node_id = feed.node_id)
            LEFT JOIN xf_user AS user ON
                (user.user_id = feed.user_id)
            WHERE feed.feed_id = ?
        '
$feedId);
    }

    
/**
     * Fetch feeds matching the parameters specified
     *
     * @param array $conditions
     * @param array $fetchOptions (unused at present)
     *
     * @return array
     */
    
public function getFeeds(array $conditions = array(), array $fetchOptions = array())
    {
        
$whereConditions $this->prepareFeedConditions($conditions$fetchOptions);

        switch (!empty(
$fetchOptions['order']) ? $fetchOptions['order'] : '')
        {
            case 
'last_fetch':
            case 
'title':
                
$orderBy 'feed.' $fetchOptions['order'];
                break;

            default:
                
$orderBy 'feed.title';
        }

        return 
$this->fetchAllKeyed('
            SELECT feed.*, IF(feed.active AND node.node_id, 1, 0) AS active,
                node.title AS node_title, node.node_name,
                user.username
            FROM xf_feed AS feed
            LEFT JOIN xf_node AS node ON
                (node.node_id = feed.node_id)
            LEFT JOIN xf_user AS user ON
                (user.user_id = feed.user_id)
            WHERE ' 
$whereConditions '
            ORDER BY ' 
$orderBy '
        '
'feed_id');
    }

    
/**
     * Fetch all feed records with no conditions
     *
     * @return array
     */
    
public function getAllFeeds()
    {
        return 
$this->getFeeds();
    }

    
/**
     * Prepares the SQL WHERE conditions for getFeeds()
     *
     * @param array $conditions
     * @param array $fetchOptions
     *
     * @return string
     */
    
public function prepareFeedConditions(array $conditions, array &$fetchOptions)
    {
        
$sqlConditions = array();

        if (!empty(
$conditions['time_now']))
        {
            
$sqlConditions[] = "feed.last_fetch + feed.frequency < " intval($conditions['time_now']);
        }

        if (isset(
$conditions['active']))
        {
            
$sqlConditions[] = 'feed.active = ' . ($conditions['active'] ? 0) . ' AND node.node_id IS NOT NULL';
        }

        return 
$this->getConditionsForClause($sqlConditions);
    }

    
/**
     * Fetch all feeds that are due to be fetched,
     * based on the current time and the last_fetch
     * time for each feed with its frequency.
     *
     * @return array
     */
    
protected function _getDueFeeds()
    {
        return 
$this->getFeeds(array(
            
'time_now' => XenForo_Application::$time,
            
'active' => true,
        ), array(
'order' => 'feed.last_fetch'));
    }

    
/**
     * Returns an array containing the default non-empty data for a new feed
     *
     * @return array
     */
    
public function getDefaultFeedArray()
    {
        return array(
            
// non-empty values
            
'frequency' => 1800,
            
'discussion_visible' => 1,
            
'discussion_open' => 1,

            
// empty values
            
'title' => '',
            
'url' => '',
            
'node_id' => 0,
            
'user_id' => 0,
            
'title_template' => '',
            
'message_template' => '{content}' "nn" '[url="{link}"]' . new XenForo_Phrase('continue_reading') . '[/url]',
            
'discussion_sticky' => 0,
            
'last_fetch' => 0
        
);
    }

    
/**
     * Fetch the latest data for a feed from its specified URL.
     * Individual entries are returned in the 'entries' key of the return array.
     *
     * @param string $url
     * @param Exception|null $e Exception that occurs when reading feed
     *
     * @return array
     */
    
public function getFeedData($urlException &$e null)
    {
        try
        {
            
$feed Zend_Feed_Reader::import($url);
        }
        catch (
Exception $feedEx)
        {
            
$e $feedEx;
            return array();
        }

        
$data = array(
            
'id' => $feed->getId(),
            
'title' => $feed->getTitle(),
            
'link' => $feed->getLink(),
            
'date_modified' => $feed->getDateModified(),
            
'description' => $feed->getDescription(),
            
'language' => $feed->getLanguage(),
            
'image' => $feed->getImage(),
            
'generator' => $feed->getGenerator(),
            
'entries' => array()
        );

        foreach (
$feed as $entry)
        {
            
/** @var $entry Zend_Feed_Reader_EntryInterface */

            
$entryData = array(
                
'id' => $entry->getId(),
                
'title' => html_entity_decode($entry->getTitle(), ENT_COMPAT'utf-8'),
                
'description' => html_entity_decode($entry->getDescription(), ENT_COMPAT'utf-8'),
                
'date_modified' => null,
                
'authors' => $entry->getAuthors(),
                
'link' => $entry->getLink(),
                
'content_html' => $entry->getContent()
            );

            
$enclosure $entry->getEnclosure();
            if (
$enclosure)
            {
                
$entryData['enclosure_url'] = $enclosure->url;
                
$entryData['enclosure_length'] = $enclosure->length;
                
$entryData['enclosure_type'] = $enclosure->type;
            }

            if (
utf8_strlen($entryData['id']) > 250)
            {
                
$entryData['id'] = md5($entryData['id']);
            }

            try
            {
                
$entryData['date_modified'] = $entry->getDateModified();
            }
            catch (
Zend_Exception $e) {} // triggered with invalid date format

            
if (!empty($entryData['date_modified']) && $entryData['date_modified'] instanceof Zend_Date)
            {
                
$entryData['date_modified'] = $entryData['date_modified']->getTimeStamp();
            }
            else
            {
                
$entryData['date_modified'] = XenForo_Application::$time;
            }

            
$entryData['date_modified'] = XenForo_Locale::dateTime($entryData['date_modified'], 'absolute');

            
$data['entries'][] = $entryData;
        }

        return 
$data;
    }

    
/**
     * Prepares data fetched from getFeedData() for use in posts
     *
     * @param array $feedData
     * @param array $feed
     *
     * @return array
     */
    
public function prepareFeedData(array $feedData, array $feed)
    {
        
$feed['baseUrl'] = $this->getFeedBaseUrl($feed['url']);

        foreach (
$feedData['entries'] AS &$entry)
        {
            
$entry $this->prepareFeedEntry($entry$feedData$feed);
        }

        return 
$feedData;
    }

    
/**
     * Prepares the data from a single feed entry for use in posts
     *
     * @param array $entry
     * @param array $feedData
     * @param array $feed
     *
     * @return array
     */
    
public function prepareFeedEntry(array $entry, array $feedData, array $feed)
    {
        
$html $entry['content_html'];
        
$html preg_replace('#<p#i''<br>$0'$html);
        
$html preg_replace('#</p>(?!s*<br)#i''$0<br>'$html);

        
$entry['content'] = XenForo_Html_Renderer_BbCode::renderFromHtml($html, array(
            
'baseUrl' => $feed['baseUrl']
        ));

        
$entry['author'] = $this->_getAuthorNamesFromArray($entry['authors']);

        if (empty(
$feed['message_template']))
        {
            
$entry['message'] = $entry['content'];
        }
        else
        {
            
$entry['message'] = $this->_replaceTokens($feed['message_template'], $entry);
        }

        
$entry['message'] = trim($entry['message']);
        if (
$entry['message'] === '')
        {
            
$entry['message'] = '[url]' $entry['link'] . '[/url]';
        }

        if (!empty(
$feed['title_template']))
        {
            
$entry['title'] = $this->_replaceTokens($feed['title_template'], $entry);
        }

        return 
$entry;
    }

    
/**
     * Fetch the base URL for the feed
     *
     * @param string URL
     *
     * @return string Base URL
     */
    
public function getFeedBaseUrl($url)
    {
        return 
dirname($url);
    }

    
/**
     * Searches the given template string for {token} and replaces it with $entry[token]
     *
     * @param string $template
     * @param array $entry
     */
    
protected function _replaceTokens($template, array $entry)
    {
        if (
preg_match_all('/{([a-z0-9_]+)}/i'$template$matches))
        {
            foreach (
$matches[1] AS $token)
            {
                if (isset(
$entry[$token]))
                {
                    
$template str_replace('{' $token '}'$entry[$token], $template);
                }
            }
        }

        return 
$template;
    }

    
/**
     * Attempts to convert a Zend_Feed_Reader_Collection_Author object
     * into a comma-separated list of names.
     *
     * @param Zend_Feed_Reader_Collection_Author|null $feedAuthors
     *
     * @return string
     */
    
protected function _getAuthorNamesFromArray($feedAuthors)
    {
        
$authorNames = array();

        if (
$feedAuthors)
        {
            foreach (
$feedAuthors AS $author)
            {
                if (isset(
$author['name']))
                {
                    
$authorNames[] = $author['name'];
                }
                else if (isset(
$author['email']))
                {
                    
$authorNames[] = $author['email'];
                }
            }
        }

        return 
implode(', '$authorNames);
    }

    
/**
     * Returns the array of possible frequency values
     *
     * @return array
     */
    
public function getFrequencyValues()
    {
        return 
self::$_frequencyValues;
    }

    
/**
     * Checks the current feed data against a list of already-imported entries
     * and removes any entries from the data that have already been imported.
     *
     * @param array $feedData
     * @param array $feed
     *
     * @return array
     */
    
protected function _checkProcessedEntries(array $feedData, array $feed)
    {
        
$ids = array();

        foreach (
$feedData['entries'] AS $i => &$entry)
        {
            
$ids[$entry['id']] = $i;

            
$entry['hash'] = md5($entry['id'] . $entry['title'] . $entry['content_html']);
        }

        if (!
$ids)
        {
            return 
$feedData;
        }

        
$existing $this->_getDb()->fetchCol('
            SELECT unique_id
            FROM xf_feed_log
            WHERE feed_id = ?
                AND unique_id IN (' 
$this->_getDb()->quote(array_keys($ids)) . ')
        '
$feed['feed_id']);

        foreach (
$existing AS $id)
        {
            if (isset(
$ids[$id]))
            {
                unset(
$feedData['entries'][$ids[$id]]);
            }
        }

        
$feedData['entries'] = array_reverse($feedData['entries']); // post the newest stuff at the end
        
$feedData['entries'] = $this->_limitEntries($feedData['entries'], self::$_maxEntriesPerImport);

        return 
$feedData;
    }

    
/**
     * Limits the number of entries in the given array to the number specified.
     *
     * @param array $entries
     * @param integer $maxEntries
     */
    
protected function _limitEntries(array $entries$maxEntries 0)
    {
        if (
$maxEntries)
        {
            return 
array_slice($entries0$maxEntriestrue);
        }
        else
        {
            return 
$entries;
        }
    }

    
/**
     * Inserts the data of a single feed entry
     *
     * @param array $entryData
     * @param array $feedData
     * @param array $feed
     *
     * @return integer|boolean Thread ID on success, false on failure
     */
    
protected function _insertFeedEntry(array $entryData, array $feedData, array $feed)
    {
        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        
$writer XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread'XenForo_DataWriter::ERROR_SILENT);
        
$writer->setOption(XenForo_DataWriter_Discussion::OPTION_TRIM_TITLEtrue);
        
$writer->bulkSet(array(
            
'node_id' => $feed['node_id'],
            
'prefix_id' => $feed['prefix_id'],
            
'discussion_state' => $feed['discussion_visible'] ? 'visible' 'moderated',
            
'discussion_open' => $feed['discussion_open'],
            
'sticky' => $feed['discussion_sticky'],
            
'title' => $entryData['title'],
            
'user_id' => $feed['user_id'],
        ));

        
// TODO: The wholeWordTrim() used here may not be exactly ideal. Any better ideas?
        
if ($feed['user_id'])
        {
            
// post as the specified registered user
            
$writer->set('username'$feed['username']);
        }
        else if (
$entryData['author'])
        {
            
// post as guest, using the author name(s) from the entry
            
$writer->set('username'XenForo_Helper_String::wholeWordTrim($entryData['author'], 250''));
        }
        else
        {
            
// post as guest, using the feed title
            
$writer->set('username'XenForo_Helper_String::wholeWordTrim($feed['title'], 250''));
        }

        
$postWriter $writer->getFirstMessageDw();
        
$postWriter->setOption(XenForo_DataWriter_DiscussionMessage::OPTION_IS_AUTOMATEDtrue);
        
$postWriter->setOption(XenForo_DataWriter_DiscussionMessage::OPTION_VERIFY_GUEST_USERNAMEfalse);
        
$postWriter->set('message'$entryData['message']);

        
$writer->save();

        
$threadId $writer->get('thread_id');

        if (
$threadId)
        {
            try
            {
                
$db->query('
                    INSERT INTO xf_feed_log
                        (feed_id, unique_id, hash, thread_id)
                    VALUES
                        (?, ?, ?, ?)
                '
, array($feed['feed_id'], utf8_substr($entryData['id'], 0250), $entryData['hash'], $threadId));
            }
            catch (
Zend_Db_Exception $e)
            {
                
XenForo_Db::rollback($db);
                return 
false;
            }
        }

        
XenForo_Db::commit($db);

        return 
$threadId;
    }

    
/**
     * Inserts the data from a single feed.
     *
     * @param array $feedData
     * @param array $feed
     */
    
protected function _insertFeedData(array $feedData, array $feed)
    {
        
// insert feed data and update feed
        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        foreach (
$feedData['entries'] AS $entry)
        {
            
$this->_insertFeedEntry($entry$feedData$feed);
        }

        
// update feed
        
$dw XenForo_DataWriter::create('XenForo_DataWriter_Feed');
        
$dw->setExistingData($feed['feed_id']);
        
$dw->set('last_fetch'XenForo_Application::$time);
        
$dw->save();

        
XenForo_Db::commit($db);
    }

    
/**
     * Prepares and inserts the data from a single feed
     *
     * @param array $feed
     */
    
public function importFeedData(array $feed)
    {
        
$feedData $this->getFeedData($feed['url']);
        if (!
$feedData)
        {
            return;
        }
        
$feedData $this->_checkProcessedEntries($feedData$feed); // filter dupes
        
$feedData $this->prepareFeedData($feedData$feed);

        
$this->_insertFeedData($feedData$feed);
    }

    
/**
     * Prepares and imports the data from all feeds due to be imported
     */
    
public function scheduledImport()
    {
        foreach (
$this->_getDueFeeds() AS $feedId => $feed)
        {
            
$this->importFeedData($feed);
        }
    }

    
/**
     * Prepares and imports the data from all feeds due to be imported
     *
     * @param $maxRunTime integer Maximum number of seconds this can run for. Note that this can potentially be over run by a slow requst.
     */
    
public function scheduledImportTimeLimited($maxRunTime)
    {
        
$s microtime(true);

        foreach (
$this->_getDueFeeds() AS $feedId => $feed)
        {
            
$this->importFeedData($feed);

            if (
$maxRunTime && microtime(true) - $s $maxRunTime)
            {
                break;
            }
        }
    }

    
/**
     * Fetch feeds for ACP quick search based on title match
     *
     * @param string $searchText
     *
     * @return array
     */
    
public function getFeedsForAdminQuickSearch($searchText)
    {
        
$quotedString XenForo_Db::quoteLike($searchText'lr'$this->_getDb());

        return 
$this->fetchAllKeyed('
            SELECT * FROM xf_feed
            WHERE title LIKE ' 
$quotedString '
            ORDER BY active, title'
,
        
'feed_id');
    }
}
Онлайн: 1
Реклама