Index: feeds.plugins.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.plugins.inc,v retrieving revision 1.6 diff -r1.6 feeds.plugins.inc 149c149 < 'parent' => 'FeedsProcessor', --- > 'parent' => 'FeedsNodeProcessor', Index: plugins/FeedsFeedNodeProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsFeedNodeProcessor.inc,v retrieving revision 1.14 diff -r1.14 FeedsFeedNodeProcessor.inc 13,56c13 < class FeedsFeedNodeProcessor extends FeedsProcessor { < < /** < * Implementation of FeedsProcessor::process(). < */ < public function process(FeedsImportBatch $batch, FeedsSource $source) { < while ($item = $batch->shiftItem()) { < < // If the target item does not exist OR if update_existing is enabled, < // map and save. < if (!$nid = $this->existingItemId($item, $source) || $this->config['update_existing']) { < < // Map item to a node. < $node = $this->map($item); < < // If updating populate nid and vid avoiding an expensive node_load(). < if (!empty($nid)) { < $node->nid = $nid; < $node->vid = db_result(db_query("SELECT vid FROM {node} WHERE nid = %d", $nid)); < } < < // Save the node. < node_save($node); < < if ($nid) { < $batch->updated++; < } < else { < $batch->created++; < } < } < } < < // Set messages. < if ($batch->created) { < drupal_set_message(format_plural($batch->created, 'Created @number @type node.', 'Created @number @type nodes.', array('@number' => $batch->created, '@type' => $this->config['content_type']))); < } < elseif ($batch->updated) { < drupal_set_message(format_plural($batch->updated, 'Updated @number @type node.', 'Updated @number @type nodes.', array('@number' => $batch->updated, '@type' => $this->config['content_type']))); < } < else { < drupal_set_message(t('There is no new content.')); < } < } --- > class FeedsFeedNodeProcessor extends FeedsNodeProcessor { 71,72c28 < protected function map($source_item) { < --- > protected function map($source_item, $target_node) { 79,80d34 < $target_node = new stdClass(); < $target_node->type = $this->config['content_type']; 84d37 < node_object_prepare($target_node); 104,108c57,59 < return array( < 'content_type' => '', < 'update_existing' => 0, < 'mappings' => array(), < ); --- > $defaults = parent::configDefaults(); > $defaults['content_type'] = ''; // reset content type > return $defaults; 114a66,67 > $form = parent::configForm($form_state); > 134c87 < '#description' => t('Choose node type to create from this feed. Only node types with attached importer configurations are listed here. Note: Users with "import !feed_id feeds" permissions will be able to import nodes of the content type selected here regardless of the node level permissions. However, users with "clear !feed_id permissions" need to have sufficient node level permissions to delete the imported nodes.', array('!feed_id' => $this->id)), --- > '#description' => t('Choose default node type to create from this feed. Only node types with attached importer configurations are listed here. Note: Users with "import !feed_id feeds" permissions will be able to import nodes of the content type selected here regardless of the node level permissions. However, users with "clear !feed_id permissions" need to have sufficient node level permissions to delete the imported nodes.', array('!feed_id' => $this->id)), 138,144d90 < // @todo Implement real updating. < $form['update_existing'] = array( < '#type' => 'checkbox', < '#title' => t('Replace existing feed nodes'), < '#description' => t('If an existing node is found for an imported node, replace it. Existing nodes will be determined using mappings that are a "unique target".'), < '#default_value' => $this->config['update_existing'], < ); 152c98,99 < if ($target_element == 'source') { --- > parent::setTargetElement($target_node, $target_element, $value); > if ($target_element == 'url') { 158,163c105,113 < elseif ($target_element == 'body') { < $target_node->teaser = $value; < $target_node->body = $value; < } < elseif (in_array($target_element, array('title', 'status', 'created'))) { < $target_node->$target_element = $value; --- > elseif ($target_element == 'type') { > if (feeds_get_importer_id($value)) { > // only accept node types with attached import configuration > $target_node->type = $value; > } > else { > // set default value > $target_node->type = $this->config['content_type']; > } 171,206c121,127 < $targets = array( < 'title' => array( < 'name' => t('Title'), < 'description' => t('The title of the feed node.'), < ), < 'status' => array( < 'name' => t('Published status'), < 'description' => t('Whether a feed node is published or not. 1 stands for published, 0 for not published.'), < ), < 'created' => array( < 'name' => t('Published date'), < 'description' => t('The UNIX time when a node has been published.'), < ), < 'body' => array( < 'name' => t('Body'), < 'description' => t('The body of the node. The teaser will be the same as the entire body.'), < ), < 'source' => array( < 'name' => t('Feed source'), < 'description' => t('Depending on the selected fetcher, this could be for example a URL or a path to a file.'), < 'optional_unique' => TRUE, < ), < ); < return $targets; < } < < /** < * Get nid of an existing feed item node if available. < */ < protected function existingItemId($source_item, FeedsSource $source) { < < // We only support one unique target: source < foreach ($this->uniqueTargets($source_item) as $target => $value) { < if ($target == 'source') { < return db_result(db_query("SELECT fs.feed_nid FROM {node} n JOIN {feeds_source} fs ON n.nid = fs.feed_nid WHERE fs.id = '%s' AND fs.source = '%s'", $this->feedNodeImporter()->id, $value)); < } --- > if (empty($this->config['content_type'])) { > // issue warning > drupal_set_message(t('No default content type selected'), 'warning'); > return array(); > } > else { > return parent::getMappingTargets(); 208d128 < return 0; Index: tests/feeds.test =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/tests/feeds.test,v retrieving revision 1.23 diff -r1.23 feeds.test 1033a1034,1212 > > /** > * Test importing OPML that creates feed nodes. > */ > class FeedsOPMLToFeedNodesTest extends FeedsWebTestCase { > > /** > * Describe this test. > */ > public function getInfo() { > return array( > 'name' => t('OPML import to feed nodes.'), > 'description' => t('Tests a feed configuration with file import, uses OPML parser and a feed node processor.'), > 'group' => t('Feeds'), > ); > } > > /** > * Set up test. > */ > public function setUp() { > parent::setUp('feeds', 'feeds_ui', 'ctools'); > $this->drupalLogin( > $this->drupalCreateUser( > array( > 'administer feeds', 'administer nodes', > ) > ) > ); > } > > /** > * Generate an OPML test feed that points to RSS feeds. > */ > public function generateOPML() { > $path = $GLOBALS['base_url'] .'/'. drupal_get_path('module', 'feeds') .'/tests/feeds/'; > > $output = > ' > > > OPML > Fri, 16 Oct 2009 02:53:17 GMT > Nuvole > > > > > > > > '; > > // UTF 8 encode output string and write it to disk > $output = utf8_encode($output); > $file = $this->absolute() .'/'. file_directory_path() .'/test-opml-'. $this->randomName() .'.opml'; > $handle = fopen($file, 'w'); > fwrite($handle, $output); > fclose($handle); > return $file; > } > > /** > * Test feedfeed node and feed node creation. > */ > public function test() { > $this->createFeed('feed'); > $this->createFeedFeed('feedfeed', 'feed'); > > // Import OPML and assert. > $file = $this->generateOPML(); > $this->importFile('feedfeed', $file); > $count = db_result(db_query('SELECT COUNT(*) FROM {feeds_source}')); > // Check that we have 3 feeds added (the feedfeed + two created feeds) > $this->assertEqual($count, 3, 'Found '. $count .' number of items.'); > > // Assert DB status for feed nodes > $count = db_result(db_query('SELECT COUNT(*) FROM {feeds_node_item} WHERE id="%s"', 'feedfeed')); > $this->assertEqual($count, 2, 'Found '. $count .' number of items.'); > > // run cron so that feeds import nodes > $this->drupalGet('cron.php'); > > // Assert DB status for feed nodes. > $count = db_result(db_query('SELECT COUNT(*) FROM {feeds_node_item} WHERE id="%s"', 'feed')); > $this->assertEqual($count, 35, 'Accurate number of items in database: '. $count); > } > > public function createFeedFeed($id, $feed_id) { > // Create our master feed that will import a listing of feeds. > $this->createFeedConfiguration('Import OPML files generating feed node types.', $id); > > // Set and configure plugins. > $this->setPlugin($id, 'FeedsFileFetcher'); > $this->setPlugin($id, 'FeedsOPMLParser'); > $this->setPlugin($id, 'FeedsFeedNodeProcessor'); > > // Change feed node type to recently created 'feed'. > $edit = array( > 'content_type' => $feed_id, > ); > $this->drupalPost('admin/build/feeds/edit/' . $id .'/settings/FeedsFeedNodeProcessor', $edit, 'Save'); > > // Change some of the basic configuration. > $edit = array( > 'content_type' => '', // don't attach > 'import_period' => FEEDS_SCHEDULE_NEVER, > ); > $this->drupalPost('admin/build/feeds/edit/'. $id .'/settings', $edit, 'Save'); > > // Add mappings > $this->addMappings($id, > array( > array( > 'source' => 'title', > 'target' => 'title', > 'unique' => FALSE, > ), > array( > 'source' => 'xmlurl', > 'target' => 'url', > 'unique' => TRUE, > ) > ) > ); > } > > public function createFeed($id) { > // Create content type > $this->drupalCreateContentType(array('type' => $id)); > > // Create feed. > $this->createFeedConfiguration('Feed', $id); > > // Set and configure plugins. > $this->setPlugin($id, 'FeedsHTTPFetcher'); > $this->setPlugin($id, 'FeedsSyndicationParser'); > $this->setPlugin($id, 'FeedsNodeProcessor'); > > // Change some of the basic configuration. > $edit = array( > 'content_type' => $id, > 'import_period' => 0, // we need to schedule import as often as possible, as import on create is disabled here > 'import_on_create' => 1, > ); > $this->drupalPost('admin/build/feeds/edit/' . $id .'/settings', $edit, 'Save'); > > // Add mappings > $this->addMappings($id, > array( > array( > 'source' => 'title', > 'target' => 'title', > 'unique' => FALSE, > ), > array( > 'source' => 'description', > 'target' => 'body', > 'unique' => FALSE, > ), > array( > 'source' => 'timestamp', > 'target' => 'created', > 'unique' => FALSE, > ), > array( > 'source' => 'url', > 'target' => 'url', > 'unique' => TRUE, > ), > array( > 'source' => 'guid', > 'target' => 'guid', > 'unique' => TRUE, > ), > ) > ); > } > }