? 641522-7_enclosure_and_results_interfaces.patch ? 641522_4_enclosure_and_results_interfaces.patch ? libraries/simplepie.inc Index: feeds.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.module,v retrieving revision 1.20 diff -u -p -r1.20 feeds.module --- feeds.module 16 Nov 2009 14:52:38 -0000 1.20 +++ feeds.module 3 Dec 2009 22:09:55 -0000 @@ -257,14 +257,14 @@ function feeds_nodeapi(&$node, $op, $for $source->addConfig($node->feeds); $result = $importer->fetcher->fetch($source); $result = $importer->parser->parse($result, $source); - if (!isset($result->value['title']) || trim($result->value['title']) == '') { - form_set_error('title', t('Could not retrieve title from feed.'), 'error'); - } - else { + if ($result instanceof FeedsSyndicationParserResultInterface && $title = trim($result->getTitle())) { // Keep the title in a static cache and populate $node->title on // 'presave' as node module looses any changes to $node after // 'validate'. - $last_title = $result->value['title']; + $last_title = $title; + } + else { + form_set_error('title', t('Could not retrieve title from feed.'), 'error'); } } catch (Exception $e) { Index: includes/FeedsImporter.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/includes/FeedsImporter.inc,v retrieving revision 1.4 diff -u -p -r1.4 FeedsImporter.inc --- includes/FeedsImporter.inc 21 Oct 2009 22:49:47 -0000 1.4 +++ includes/FeedsImporter.inc 3 Dec 2009 22:09:56 -0000 @@ -11,54 +11,77 @@ require_once(dirname(__FILE__) .'/FeedsC require_once(dirname(__FILE__) .'/FeedsSource.inc'); /** - * A Feeds result class. + * Abstraction of a file handled by Feeds. * - * @see class FeedsFetcherResult - * @see class FeedsParserResult */ -abstract class FeedsResult { +interface FeedsFileInterface { + /** + * @var A string containg the default mime type to use when a + * FeedsFileInterface's mime type cannot be determined. + */ + const DEFAULT_MIME_TYPE = 'application/octet-stream'; - // An array of valid values for $type. - protected $valid_types = array(); - // The type of this result. - protected $type; - // The value of this result. - protected $value; + /** + * Get the content of the file as a string. When the content of the file + * cannot be represented safely as a string, the implementation should throw + * an exception. + * + * @return A strign containing the content of the file. + */ + public function getContent(); /** - * Constructor: create object, validate class variables. + * Get the filename of a file for tthis FeedsFileInterface. If the actual + * file is a remote one, implementation should download it to a temporary + * local file. This allow transparent access to the file by consumers. * - * @param $value - * The value of this result. - * @param $type - * The type of this result. Must be one of $valid_types. + * @return A string containing the filename of a local file. */ - public function __construct($value, $type) { - $this->__set('type', $type); - $this->__set('value', $value); - } + public function getFile(); /** - * Control access to class variables. + * Returns the interne type of the file. The returned value must + * never be empty. Implementations should return + * application/octet-stream when a correct mime type cannot be + * determined. + * + * @return A string containing the internet media type of the file. + * + * @see FeedResultEnclosure::DEFAULT_MIME_TYPE */ - public function __set($name, $value) { - if ($name == 'valid_types') { - throw new Exception(t('Cannot write FeedsResult::valid_types.')); - } - if ($name == 'type') { - if (!in_array($value, $this->valid_types)) { - throw new Exception(t('Invalid type "!type"', array('!type' => $value))); - } - } - $this->$name = $value; - } + public function getMimeType(); +} +/** + * A Feeds result class. + * + * @see class FeedsFetcherResultInterface + * @see class FeedsParserResultInterface + */ +interface FeedsResultInterface { + +} + +/** + * Minimalist interface for enclosures (ie. a file attached to a + * FeedsResult item) + */ +interface FeedsResultEnclosureInterface extends FeedsFileInterface { /** - * Control access to class variables. + * Returns the URL for the enclosure. Any valid URL is an acceptable result. + * + * @return A string containing a valid URL (RFC 1738) for the enclosure. */ - public function __get($name) { - return $this->$name; - } + public function getUrl(); + + /** + * Returns the enclosure description. + * + * @return A string containing the description of the enclosure. + * + */ + public function getDescription(); + } /** Index: plugins/FeedsCSVParser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsCSVParser.inc,v retrieving revision 1.2 diff -u -p -r1.2 FeedsCSVParser.inc --- plugins/FeedsCSVParser.inc 20 Oct 2009 20:59:04 -0000 1.2 +++ plugins/FeedsCSVParser.inc 3 Dec 2009 22:09:56 -0000 @@ -9,16 +9,10 @@ class FeedsCSVParser extends FeedsParser /** * Parses a raw string and returns a Feed object from it. */ - public function parse(FeedsFetcherResult $fetcherResult, FeedsSource $source) { + public function parse(FeedsFetcherResultInterface $fetcherResult, FeedsSource $source) { feeds_include_library('ParserCSV.inc', 'ParserCSV'); - if ($fetcherResult->type == 'text/filepath') { - $iterator = new ParserCSVIterator(realpath($fetcherResult->value)); - } - // @todo: write string buffer iterator. - else { - throw new Exception(t('You must use CSV Parser with File Fetcher.')); - } + $iterator = new ParserCSVIterator(realpath($fetcherResult->getFile())); // Parse. $source_config = $source->getConfigFor($this); Index: plugins/FeedsDataProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsDataProcessor.inc,v retrieving revision 1.5 diff -u -p -r1.5 FeedsDataProcessor.inc --- plugins/FeedsDataProcessor.inc 3 Dec 2009 21:27:40 -0000 1.5 +++ plugins/FeedsDataProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -14,12 +14,12 @@ class FeedsDataProcessor extends FeedsPr /** * Implementation of FeedsProcessor::process(). */ - public function process(FeedsParserResult $parserResult, FeedsSource $source) { + public function process(FeedsParserResultInterface $parserResult, FeedsSource $source) { // Count number of created and updated nodes. $inserted = $updated = 0; - foreach ($parserResult->value['items'] as $item) { + foreach ($parserResult->getItems() as $item) { if (!($id = $this->existingItemId($item, $source)) || $this->config['update_existing']) { // Map item to a data record, feed_nid and timestamp are mandatory. $data = array(); Index: plugins/FeedsFeedNodeProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsFeedNodeProcessor.inc,v retrieving revision 1.4 diff -u -p -r1.4 FeedsFeedNodeProcessor.inc --- plugins/FeedsFeedNodeProcessor.inc 18 Nov 2009 16:53:48 -0000 1.4 +++ plugins/FeedsFeedNodeProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -15,12 +15,12 @@ class FeedsFeedNodeProcessor extends Fee /** * Implementation of FeedsProcessor::process(). */ - public function process(FeedsParserResult $parserResult, FeedsSource $source) { + public function process(FeedsParserResultInterface $parserResult, FeedsSource $source) { // Count number of created and updated nodes. $created = $updated = 0; - foreach ($parserResult->value['items'] as $item) { + foreach ($parserResult->getItems() as $item) { // If the target item does not exist OR if update_existing is enabled, // map and save. Index: plugins/FeedsFetcher.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsFetcher.inc,v retrieving revision 1.1 diff -u -p -r1.1 FeedsFetcher.inc --- plugins/FeedsFetcher.inc 20 Oct 2009 21:03:08 -0000 1.1 +++ plugins/FeedsFetcher.inc 3 Dec 2009 22:09:56 -0000 @@ -4,12 +4,8 @@ /** * Defines the object a Fetcher returns on fetch(). */ -class FeedsFetcherResult extends FeedsResult { - // Define valid types. - // @todo: does text/filepath make sense? - // @todo: If convenient, we could expand on this concept and build content - // type negotiation between Fetchers and Parsers. - protected $valid_types = array('text/filepath', 'text/xml'); +interface FeedsFetcherResultInterface extends FeedsResultInterface, FeedsFileInterface { + } /** Index: plugins/FeedsFileFetcher.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsFileFetcher.inc,v retrieving revision 1.2 diff -u -p -r1.2 FeedsFileFetcher.inc --- plugins/FeedsFileFetcher.inc 20 Oct 2009 20:59:04 -0000 1.2 +++ plugins/FeedsFileFetcher.inc 3 Dec 2009 22:09:56 -0000 @@ -18,7 +18,7 @@ class FeedsFileFetcher extends FeedsFetc $source_config = $source->getConfigFor($this); // Just return path to file, contents can be read easily with // file_get_contents($file_path); - return new FeedsFetcherResult($source_config['source'], 'text/filepath'); + return new FeedsFileFetcherResult($source_config['source']); } /** @@ -61,4 +61,29 @@ class FeedsFileFetcher extends FeedsFetc form_set_error('feeds][source', t('File needs to point to a file in your Drupal file system path.')); } } +} + +class FeedsFileFetcherResult implements FeedsFetcherResultInterface { + + private $file; + private $mime_type; + + public function __construct($file) { + $this->file = $file; + } + + public function getContent() { + return file_get_contents($this->file); + } + + public function getFile() { + return $this->file; + } + + public function getMimeType() { + if(!isset($this->mime_type)) { + $this->mime_type = file_get_mimetype($this->getFile()); + } + return $this->mime_type; + } } \ No newline at end of file Index: plugins/FeedsHTTPFetcher.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsHTTPFetcher.inc,v retrieving revision 1.5 diff -u -p -r1.5 FeedsHTTPFetcher.inc --- plugins/FeedsHTTPFetcher.inc 17 Nov 2009 20:14:30 -0000 1.5 +++ plugins/FeedsHTTPFetcher.inc 3 Dec 2009 22:09:56 -0000 @@ -34,7 +34,7 @@ class FeedsHTTPFetcher extends FeedsFetc if ($result->code != 200) { throw new Exception(t('Download of @url failed with code !code.', array('@url' => $url, '!code' => $result->code))); } - return new FeedsFetcherResult($result->data, 'text/xml'); + return new FeedsHTTPFetcherResult($result->data, 'text/xml'); } /** @@ -87,3 +87,34 @@ class FeedsHTTPFetcher extends FeedsFetc } } +class FeedsHTTPFetcherResult implements FeedsFetcherResultInterface { + + private $content; + private $file; + private $mime_type; + + public function __construct($content, $mime_type = 'application/octet-stream') { + $this->content = $content; + $this->mime_type = $mime_type; + } + + public function getContent() { + return $this->content; + } + + public function getFile() { + if(!isset($this->file)) { + //@todo get extension from mime_type + $dest = file_destination(file_directory_temp() . '/' . get_class($this). '.tmp', FILE_EXISTS_RENAME); + $file = file_save_data($this->content, $dest); + if($file === 0) { + throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest))); + } + } + return $this->file; + } + + public function getMimeType() { + return $this->mime_type; + } +} Index: plugins/FeedsNodeProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsNodeProcessor.inc,v retrieving revision 1.17 diff -u -p -r1.17 FeedsNodeProcessor.inc --- plugins/FeedsNodeProcessor.inc 3 Dec 2009 20:55:05 -0000 1.17 +++ plugins/FeedsNodeProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -14,12 +14,12 @@ class FeedsNodeProcessor extends FeedsPr /** * Implementation of FeedsProcessor::process(). */ - public function process(FeedsParserResult $parserResult, FeedsSource $source) { + public function process(FeedsParserResultInterface $parserResult, FeedsSource $source) { // Count number of created and updated nodes. $created = $updated = 0; - foreach ($parserResult->value['items'] as $item) { + foreach ($parserResult->getItems() as $item) { // Create/update if item does not exist or update existing is enabled. if (!($nid = $this->existingItemId($item, $source)) || $this->config['update_existing']) { Index: plugins/FeedsOPMLParser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsOPMLParser.inc,v retrieving revision 1.2 diff -u -p -r1.2 FeedsOPMLParser.inc --- plugins/FeedsOPMLParser.inc 2 Nov 2009 20:22:03 -0000 1.2 +++ plugins/FeedsOPMLParser.inc 3 Dec 2009 22:09:56 -0000 @@ -14,13 +14,8 @@ class FeedsOPMLParser extends FeedsParse /** * Parses a raw string and returns a Feed object from it. */ - public function parse(FeedsFetcherResult $fetcherResult, FeedsSource $source) { - if ($fetcherResult->type == 'text/filepath') { - $string = file_get_contents($fetcherResult->value); - } - else { - $string = $fetcherResult->value; - } + public function parse(FeedsFetcherResultInterface $fetcherResult, FeedsSource $source) { + $string = $fetcherResult->getContent(); feeds_include_library('opml_parser.inc', 'opml_parser'); return new FeedsParserResult(opml_parser_parse($string), 'syndication'); } Index: plugins/FeedsParser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsParser.inc,v retrieving revision 1.1 diff -u -p -r1.1 FeedsParser.inc --- plugins/FeedsParser.inc 20 Oct 2009 21:03:08 -0000 1.1 +++ plugins/FeedsParser.inc 3 Dec 2009 22:09:56 -0000 @@ -2,20 +2,100 @@ // $Id: FeedsParser.inc,v 1.1 2009/10/20 21:03:08 alexb Exp $ /** - * Defines the object a Parser returns on parser(). + * A parsed feed, the result of a FeedsParser::process. + * + * @see FeedsParser::process() */ -class FeedsParserResult extends FeedsResult { - // Define valid types. - // @todo: does this distinction make sense? We may be able to run with - // 'simple' and no special case for 'syndication'. - protected $valid_types = array('simple', 'syndication'); +interface FeedsParserResultInterface extends FeedsResultInterface { + /** + *@todo Can this method return an object implementing + * the ArrayAccess, Traversable and Countable interfaces ? + * is_array() on such an object returns FALSE. If support for object + * is announced, consumers have to double check with is_array() + * and instanceof. + * + * @return An array containing the feed items. + */ + public function getItems(); +} + +/*** + * Simple FeedsParserResultInterface implementation. + */ +class FeedsParserResult implements FeedsParserResultInterface { + + private $items; /** - * Override constructor to define a default type. + * + * Create a new FeedsBaseParserResult. Set properties result from an array. + * + * @param $value + * An array containing the various property of the FeedsResult. */ - public function __construct($value, $type = 'simple') { - parent::__construct($value, $type); + public function __construct($value) { + $this->items = isset($value['items']) ? $value['items'] : array(); } + + public function getItems() { + return $this->items; + } +} + +interface FeedsSyndicationParserResultInterface extends FeedsParserResultInterface { + /** + * + * @return A string containing the link of the feed. + */ + public function getLink(); + + /** + * + * @return A string containing the description of the feed. + */ + public function getDescription(); + + /** + * + * @return A string containing the title of the feed. + */ + public function getTitle(); +} + +/*** + * Simple FeedsSyndicationParserResultInterface implementation. + */ +class FeedsSyndicationParserResult extends FeedsParserResult implements FeedsSyndicationParserResultInterface { + private $link; + private $title; + private $description; + + /** + * + * Create a new FeedsBaseParserResult. Set properties result from an array. + * + * @param $value + * An array containing the various property of the FeedsResult. + */ + public function __construct($value) { + parent::__construct($value); + foreach(array('link', 'title', 'description') as $p) { + $this->{$p} = isset($value[$p]) ? $value[$p] : ''; + } + } + + public function getLink() { + return $this->link; + } + + public function getDescription() { + return $this->description; + } + + public function getTitle() { + return $this->title; + } + } /** @@ -40,7 +120,7 @@ abstract class FeedsParser extends Feeds * * @todo: Should it be execute() ? */ - public abstract function parse(FeedsFetcherResult $fetcherResult, FeedsSource $source); + public abstract function parse(FeedsFetcherResultInterface $fetcherResult, FeedsSource $source); /** * Clear all caches for results for given source. Index: plugins/FeedsProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsProcessor.inc,v retrieving revision 1.4 diff -u -p -r1.4 FeedsProcessor.inc --- plugins/FeedsProcessor.inc 31 Oct 2009 14:30:27 -0000 1.4 +++ plugins/FeedsProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -19,7 +19,7 @@ abstract class FeedsProcessor extends Fe * * @todo: Should it be execute()? */ - public abstract function process(FeedsParserResult $parserResult, FeedsSource $source); + public abstract function process(FeedsParserResultInterface $parserResult, FeedsSource $source); /** * Remove all stored results or stored results up to a certain time for this Index: plugins/FeedsSimplePieParser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsSimplePieParser.inc,v retrieving revision 1.4 diff -u -p -r1.4 FeedsSimplePieParser.inc --- plugins/FeedsSimplePieParser.inc 2 Nov 2009 20:05:10 -0000 1.4 +++ plugins/FeedsSimplePieParser.inc 3 Dec 2009 22:09:56 -0000 @@ -11,13 +11,9 @@ class FeedsSimplePieParser extends Feeds /** * Parses a raw string and returns a Feed object from it. */ - public function parse(FeedsFetcherResult $fetcherResult, FeedsSource $source) { - if ($fetcherResult->type == 'text/filepath') { - $string = file_get_contents($fetcherResult->value); - } - else { - $string = $fetcherResult->value; - } + public function parse(FeedsFetcherResultInterface $fetcherResult, FeedsSource $source) { + $string = $fetcherResult->getContent(); + feeds_include_library('simplepie.inc', 'simplepie'); // Initialize SimplePie. @@ -59,16 +55,8 @@ class FeedsSimplePieParser extends Feeds $item['author_link'] = $author->link; $item['author_email'] = $author->email; // Enclosures - $enclosures = $simplepie_item->get_enclosures(); - if (is_array($enclosures)) { - foreach ($enclosures as $enclosure) { - $mime = $enclosure->get_real_type(); - if ($mime != '') { - list($type, $subtype) = split('/', $mime); - $item['enclosures'][$type][$subtype][] = $enclosure; - } - } - } + //Store the raw enclosures in the item, let getSourceElement handled it later (if needed) + $item['enclosures'] = $simplepie_item->get_enclosures(); // Location $latitude = $simplepie_item->get_latitude(); $longitude = $simplepie_item->get_longitude(); @@ -99,8 +87,14 @@ class FeedsSimplePieParser extends Feeds $feed['items'][] = $item; } // Release parser. + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + // Workaround PHP Bug #33595, + // see http://simplepie.org/wiki/faq/i_m_getting_memory_leaks + $parser->__destruct(); + } unset($parser); - return new FeedsParserResult($feed, 'syndication'); + unset($item); + return new FeedsSyndicationParserResult($feed); } /** @@ -164,6 +158,31 @@ class FeedsSimplePieParser extends Feeds } /** + * Get an element identified by $element_key of the given item. + * The element key corresponds to the values in the array returned by + * FeedsParser::getMappingSources(). + */ + public function getSourceElement($item, $element_key) { + switch($element_key) { + case 'enclosures': + $enclosures = isset($item[$element_key]) ? $item[$element_key] : FALSE; + $result = array(); + if (is_array($enclosures)) { + foreach ($enclosures as $enclosure) { + $mime = $enclosure->get_real_type(); + if ($mime != '') { + list($type, $subtype) = split('/', $mime); + $result[$type][$subtype][] = new FeedsSimplePieEnclosure($enclosure); + } + } + } + return $result; + default: + return parent::getSourceElement($item, $element_key); + } + } + + /** * Returns cache directory. Creates it if it doesn't exist. */ protected function cacheDirectory() { @@ -181,4 +200,57 @@ class FeedsSimplePieParser extends Feeds $words = array_slice($words, 0, 3); return implode(' ', $words); } +} + +/** + * Adapter to present a SimplePie_Enclosure as a FeedResultEnclosure. + * + * @see FeedResultEnclosure + * @see SimplePie_Enclosure + */ +class FeedsSimplePieEnclosure implements FeedsResultEnclosureInterface { + private $simplepie_enclosure; + + private $file; + + function __construct(SimplePie_Enclosure $enclosure) { + $this->simplepie_enclosure = $enclosure; + } + + + public function getUrl() { + return $this->simplepie_enclosure->get_link(); + } + + + public function getDescription() { + return $this->simplepie_enclosure->get_description(); + } + + + public function getMimeType() { + $type = $this->simplepie_enclosure->get_real_type(); + return !empty($type) ? $type : FeedsFileInterface::DEFAULT_MIME_TYPE; + } + + public function getContent() { + feeds_include_library('http_request.inc', 'http_request'); + $result = http_request_get($url); + if ($result->code != 200) { + throw new Exception(t('Download of @url failed with code !code.', array('@url' => $url, '!code' => $result->code))); + } + return file_get_contents($result->data); + } + + public function getFile() { + if(!isset($this->file)) { + //@todo get extension from mime_type + $dest = file_destination(file_directory_temp() . '/' . get_class($this). '.tmp', FILE_EXISTS_RENAME); + $file = file_save_data($this->getContent(), $dest); + if($file === 0) { + throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest))); + } + } + return $this->file; + } } \ No newline at end of file Index: plugins/FeedsSyndicationParser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsSyndicationParser.inc,v retrieving revision 1.8 diff -u -p -r1.8 FeedsSyndicationParser.inc --- plugins/FeedsSyndicationParser.inc 2 Nov 2009 19:58:37 -0000 1.8 +++ plugins/FeedsSyndicationParser.inc 3 Dec 2009 22:09:56 -0000 @@ -11,15 +11,10 @@ class FeedsSyndicationParser extends Fee /** * Parses a raw string and returns a Feed object from it. */ - public function parse(FeedsFetcherResult $fetcherResult, FeedsSource $source) { - if ($fetcherResult->type == 'text/filepath') { - $string = file_get_contents($fetcherResult->value); - } - else { - $string = $fetcherResult->value; - } + public function parse(FeedsFetcherResultInterface $fetcherResult, FeedsSource $source) { + $string = $fetcherResult->getContent(); feeds_include_library('common_syndication_parser.inc', 'common_syndication_parser'); - return new FeedsParserResult(common_syndication_parser_parse($string), 'syndication'); + return new FeedsSyndicationParserResult(common_syndication_parser_parse($string)); } /** Index: plugins/FeedsTermProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsTermProcessor.inc,v retrieving revision 1.2 diff -u -p -r1.2 FeedsTermProcessor.inc --- plugins/FeedsTermProcessor.inc 2 Nov 2009 20:48:52 -0000 1.2 +++ plugins/FeedsTermProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -14,7 +14,7 @@ class FeedsTermProcessor extends FeedsPr /** * Implementation of FeedsProcessor::process(). */ - public function process(FeedsParserResult $parserResult, FeedsSource $source) { + public function process(FeedsParserResultInterface $parserResult, FeedsSource $source) { if (empty($this->config['vocabulary'])) { throw new Exception(t('You must define a vocabulary for Taxonomy term processor before importing.')); @@ -23,7 +23,7 @@ class FeedsTermProcessor extends FeedsPr // Count number of created and updated nodes. $created = $updated = $no_name = 0; - foreach ($parserResult->value['items'] as $item) { + foreach ($parserResult->getItems() as $item) { if (!($tid = $this->existingItemId($item, $source)) || $this->config['update_existing']) { Index: plugins/FeedsUserProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsUserProcessor.inc,v retrieving revision 1.2 diff -u -p -r1.2 FeedsUserProcessor.inc --- plugins/FeedsUserProcessor.inc 2 Nov 2009 20:26:57 -0000 1.2 +++ plugins/FeedsUserProcessor.inc 3 Dec 2009 22:09:56 -0000 @@ -14,12 +14,12 @@ class FeedsUserProcessor extends FeedsPr /** * Implementation of FeedsProcessor::process(). */ - public function process(FeedsParserResult $parserResult, FeedsSource $source) { + public function process(FeedsParserResultInterface $parserResult, FeedsSource $source) { // Count number of created and updated nodes. $created = $updated = $failed = 0; - foreach ($parserResult->value['items'] as $item) { + foreach ($parserResult->getItems() as $item) { if (!($uid = $this->existingItemId($item, $source)) || $this->config['update_existing']) {