diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 38e5f13acfa6211fdc39466e5d4af7144552c7d1..3875be205897f819d2a2b23d735bfe6a97531cfe 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,8 @@ Feeds 7.x 2.0 XXXXXXXXXXXXXXXXXXX --------------------------------- +- #929066 alex_b: Track all imported items. Note: All views that use 'Feeds + Item' fields or relationships need updating. - #930018 alex_b: Don't show file upload when 'Supply path directly' is selected. - #927892 alex_b: Add "Process in background" feature. Allows one-off imports to diff --git a/feeds.install b/feeds.install index 62d0fd8c480a05c56a4f51914a670c898a8e2e22..188746f4d4202b4c284310caf25192af0ada078a 100644 --- a/feeds.install +++ b/feeds.install @@ -100,27 +100,34 @@ function feeds_schema() { 'id_source' => array('id', array('source', 128)), ), ); - $schema['feeds_node_item'] = array( - 'description' => 'Stores additional information about feed item nodes. Used by FeedsNodeProcessor.', + $schema['feeds_item'] = array( + 'description' => 'Tracks items such as nodes, terms, users.', 'fields' => array( - 'nid' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The entity type.', + ), + 'entity_id' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, - 'description' => 'Primary Key: The feed item node\'s nid.', + 'description' => 'The imported entity\'s serial id.', ), 'id' => array( 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', - 'description' => 'The id of the fields object that is the producer of this item.', + 'description' => 'The id of the importer that created this item.', ), 'feed_nid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, - 'description' => 'Node id of the owner feed, if available.', + 'description' => 'Node id of the source, if available.', ), 'imported' => array( 'type' => 'int', @@ -143,46 +150,16 @@ function feeds_schema() { 'length' => 32, // The length of an MD5 hash. 'not null' => TRUE, 'default' => '', - 'description' => 'The hash of the item.', + 'description' => 'The hash of the source item.', ), ), - 'primary key' => array('nid'), + 'primary key' => array('entity_type', 'entity_id'), 'indexes' => array( 'id' => array('id'), 'feed_nid' => array('feed_nid'), + 'lookup_url' => array('entity_type', 'id', 'feed_nid', array('url', 255)), + 'lookup_guid' => array('entity_type', 'id', 'feed_nid', array('guid', 255)), 'imported' => array('imported'), - 'url' => array(array('url', 255)), - 'guid' => array(array('guid', 255)), - ), - ); - $schema['feeds_term_item'] = array( - 'description' => 'Tracks imported terms.', - 'fields' => array( - 'tid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Imported term id.', - ), - 'id' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The id of the fields object that is the creator of this item.', - ), - 'feed_nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'Node id of the owner feed, if available.', - ), - ), - 'primary key' => array('tid'), - 'indexes' => array( - 'id_feed_nid' => array('id', 'feed_nid'), - 'feed_nid' => array('feed_nid'), ), ); $schema['feeds_push_subscriptions'] = array( @@ -285,3 +262,78 @@ function feeds_update_7201(&$sandbox) { ); db_add_field('feeds_source', 'imported', $spec); } + +/** + * Create a single feeds_item table tracking all imports. + */ +function feeds_update_7202(&$sandbox) { + $spec = array( + 'description' => 'Tracks items such as nodes, terms, users.', + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The entity type.', + ), + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The imported entity\'s serial id.', + ), + 'id' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The id of the importer that created this item.', + ), + 'feed_nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Node id of the source, if available.', + ), + 'imported' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Import date of the feed item, as a Unix timestamp.', + ), + 'url' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Link to the feed item.', + ), + 'guid' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Unique identifier for the feed item.' + ), + 'hash' => array( + 'type' => 'varchar', + 'length' => 32, // The length of an MD5 hash. + 'not null' => TRUE, + 'default' => '', + 'description' => 'The hash of the source item.', + ), + ), + 'primary key' => array('entity_type', 'entity_id'), + 'indexes' => array( + 'id' => array('id'), + 'feed_nid' => array('feed_nid'), + 'lookup_url' => array('entity_type', 'id', 'feed_nid', array('url', 255)), + 'lookup_guid' => array('entity_type', 'id', 'feed_nid', array('guid', 255)), + 'imported' => array('imported'), + ), + ); + db_create_table('feeds_item', $spec); + // Copy all existing values from old tables and drop them. + $insert = "INSERT INTO {feeds_item} (entity_type, entity_id, id, feed_nid, imported, url, guid, hash)"; + db_query($insert . " SELECT 'node', nid, id, feed_nid, imported, url, guid, hash FROM {feeds_node_item}"); + db_query($insert . " SELECT 'taxonomy_term', tid, id, feed_nid, 0, '', '', '' FROM {feeds_term_item}"); + db_drop_table('feeds_node_item'); + db_drop_table('feeds_term_item'); +} diff --git a/feeds.module b/feeds.module index 6511c94cc79df200faff3e02e0300388f64c20e5..697e4fafd2d2af92dd76f8f65ecd4ee6f0fbba4d 100644 --- a/feeds.module +++ b/feeds.module @@ -396,13 +396,6 @@ function feeds_feeds_plugins() { return _feeds_feeds_plugins(); } -/** - * Implements hook_node_load(). - */ -function feeds_node_load($nodes) { - _feeds_node_processor_node_load($nodes); -} - /** * Implements hook_node_validate(). */ @@ -463,7 +456,10 @@ function feeds_node_presave($node) { * Implements hook_node_insert(). */ function feeds_node_insert($node) { - _feeds_node_processor_node_insert($node); + // Node produced by source. + feeds_item_info_insert($node, $node->nid); + + // Source attached to node. feeds_node_update($node); if ($importer_id = feeds_get_importer_id($node->type)) { $source = feeds_source($importer_id, $node->nid); @@ -481,100 +477,79 @@ function feeds_node_insert($node) { * Implements hook_node_update(). */ function feeds_node_update($node) { - _feeds_node_processor_node_update($node); - if (!$importer_id = feeds_get_importer_id($node->type)) { - return; + // Node produced by source. + feeds_item_info_update($node, $node->nid); + + // Source attached to node. + if ($importer_id = feeds_get_importer_id($node->type)) { + $source = feeds_source($importer_id, $node->nid); + $source->addConfig($node->feeds); + $source->save(); } - // Add configuration to feed source and save. - $source = feeds_source($importer_id, $node->nid); - $source->addConfig($node->feeds); - $source->save(); } /** * Implements hook_node_delete(). */ function feeds_node_delete($node) { - _feeds_node_processor_node_delete($node); + // Node produced by source. + db_delete('feeds_item') + ->condition('entity_type', 'node') + ->condition('entity_id', $node->nid) + ->execute(); + // Source attached to node. if ($importer_id = feeds_get_importer_id($node->type)) { feeds_source($importer_id, $node->nid)->delete(); } } /** - * FeedsNodeProcessor's hook_node_load(). - */ -function _feeds_node_processor_node_load($nodes) { - if ($items = db_query("SELECT nid, imported, guid, url, feed_nid FROM {feeds_node_item} WHERE nid IN (:nids)", array(':nids' => array_keys($nodes)))) { - foreach ($items as $item) { - $nodes[$item->nid]->feeds_node_item = $item; - } - } -} - -/** - * FeedsNodeProcessor's hook_node_insert(). + * Implements hook_taxonomy_term_insert(). */ -function _feeds_node_processor_node_insert($node) { - if (isset($node->feeds_node_item)) { - $node->feeds_node_item->nid = $node->nid; - drupal_write_record('feeds_node_item', $node->feeds_node_item); - } +function feeds_taxonomy_term_insert($term) { + feeds_item_info_insert($term, $term->tid); } /** - * FeedsNodeProcessor's hook_node_update(). + * Implements hook_taxonomy_term_update(). */ -function _feeds_node_processor_node_update($node) { - if (isset($node->feeds_node_item)) { - $node->feeds_node_item->nid = $node->nid; - drupal_write_record('feeds_node_item', $node->feeds_node_item, 'nid'); - } +function feeds_taxonomy_term_update($term) { + feeds_item_info_update($term, $term->tid); } /** - * FeedsNodeProcessor's hook_node_delete(). + * Implements hook_taxonomy_delete(). */ -function _feeds_node_processor_node_delete($node) { - db_delete('feeds_node_item') - ->condition('nid', $node->nid) +function feeds_taxonomy_term_delete($term) { + db_delete('feeds_item') + ->condition('entity_type', 'taxonomy_term') + ->condition('entity_id', $term->tid) ->execute(); } /** - * Implements hook_taxonomy_term_update(). + * Implements hook_user_insert(). */ -function feeds_taxonomy_term_update($term) { - if (isset($term->feeds_importer_id)) { - db_delete('feeds_term_item') - ->condition('tid', $term->tid) - ->execute(); - } +function feeds_user_insert(&$edit, $account, $category) { + feeds_item_info_insert($account, $account->uid); } /** - * Implements hook_taxonomy_term_insert(). + * Implements hook_user_update(). */ -function feeds_taxonomy_term_insert($term) { - if (isset($term->feeds_importer_id)) { - $record = array( - 'id' => $term->feeds_importer_id, - 'tid' => $term->tid, - 'feed_nid' => $term->feed_nid, - ); - drupal_write_record('feeds_term_item', $record); - } +function feeds_user_update(&$edit, $account, $category) { + feeds_item_info_update($account, $account->uid); } /** - * Implements hook_taxonomy_delete(). + * Implements hook_user_delete(). */ -function feeds_taxonomy_term_delete($term) { - db_delete('feeds_term_item') - ->condition('tid', $term->tid) +function feeds_user_delete($account) { + db_delete('feeds_item') + ->condition('entity_type', 'user') + ->condition('entity_id', $account->uid) ->execute(); } - /** * Implements hook_form_alter(). */ @@ -719,6 +694,42 @@ function feeds_dbg($msg) { } } +/** + * Loads an item info object. + * + * Example usage: + * + * $info = feeds_item_info_load('node', $node->nid); + */ +function feeds_item_info_load($entity_type, $entity_id) { + return db_select('feeds_item') + ->fields('feeds_item') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->execute() + ->fetch(); +} + +/** + * Inserts an item info object into the feeds_item table. + */ +function feeds_item_info_insert($entity, $entity_id) { + if (isset($entity->feeds_item)) { + $entity->feeds_item->entity_id = $entity_id; + drupal_write_record('feeds_item', $entity->feeds_item); + } +} + +/** + * Updates an item info object in he feeds_item table. + */ +function feeds_item_info_update($entity, $entity_id) { + if (isset($entity->feeds_item)) { + $entity->feeds_item->entity_id = $entity_id; + drupal_write_record('feeds_item', $entity->feeds_item, array('entity_type', 'entity_id')); + } +} + /** * @} */ diff --git a/feeds_import/feeds_import.test b/feeds_import/feeds_import.test index f8941cf3ba38faebcfbde812b8327bbdb52e6eda..ca3258f3e2340577e83be97abc95ebc7c4d40d4b 100644 --- a/feeds_import/feeds_import.test +++ b/feeds_import/feeds_import.test @@ -61,7 +61,7 @@ class FeedsExamplesNodeTestCase extends FeedsWebTestCase { // Assert DB status as is and again after an additional import. for ($i = 0; $i < 2; $i++) { - $count = db_query("SELECT COUNT(*) FROM {feeds_node_item}")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")->fetchField(); $this->assertEqual($count, 8, 'Found correct number of items.'); $count = db_query("SELECT COUNT(*) FROM {node} WHERE type = 'article' AND status = 1 AND uid = 0")->fetchField(); $this->assertEqual($count, 8, 'Found correct number of items.'); diff --git a/feeds_news/feeds_news.test b/feeds_news/feeds_news.test index 86874e32a482be17ef9383ffba563e210dcde120..4ef7e07c5afb0570bbcbb8de3d06de26e31b8bb0 100644 --- a/feeds_news/feeds_news.test +++ b/feeds_news/feeds_news.test @@ -50,8 +50,8 @@ class FeedsExamplesFeedTestCase extends FeedsWebTestCase { $count = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE type = 'feed_item'")); $this->assertEqual($count, 10, 'Found the correct number of feed item nodes in database.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item}")); - $this->assertEqual($count, 10, 'Found the correct number of records in feeds_node_item.'); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")); + $this->assertEqual($count, 10, 'Found the correct number of records in feeds_item.'); $count = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE title = 'Open Atrium Translation Workflow: Two Way Translation Updates'")); $this->assertEqual($count, 1, 'Found title.'); @@ -62,13 +62,13 @@ class FeedsExamplesFeedTestCase extends FeedsWebTestCase { $count = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE title = 'Scaling the Open Atrium UI'")); $this->assertEqual($count, 1, 'Found title.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item} WHERE url = 'http://developmentseed.org/blog/2009/oct/06/open-atrium-translation-workflow-two-way-updating'")); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node' AND url = 'http://developmentseed.org/blog/2009/oct/06/open-atrium-translation-workflow-two-way-updating'")); $this->assertEqual($count, 1, 'Found feed_node_item record.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item} WHERE url = 'http://developmentseed.org/blog/2009/oct/05/week-dc-tech-october-5th-edition'")); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node' AND url = 'http://developmentseed.org/blog/2009/oct/05/week-dc-tech-october-5th-edition'")); $this->assertEqual($count, 1, 'Found feed_node_item record.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item} WHERE guid = '974 at http://developmentseed.org'")); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node' AND guid = '974 at http://developmentseed.org'")); $this->assertEqual($count, 1, 'Found feed_node_item record.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item} WHERE guid = '970 at http://developmentseed.org'")); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node' AND guid = '970 at http://developmentseed.org'")); $this->assertEqual($count, 1, 'Found feed_node_item record.'); // Remove all items @@ -85,15 +85,15 @@ class FeedsExamplesFeedTestCase extends FeedsWebTestCase { $count = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE type = 'feed_item'")); $this->assertEqual($count, 0, 'Found the correct number of feed item nodes in database.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item}")); - $this->assertEqual($count, 0, 'Found the correct number of records in feeds_node_item.'); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")); + $this->assertEqual($count, 0, 'Found the correct number of records in feeds_item.'); // Create a batch of nodes. $this->createFeedNodes('feed', 10, 'feed'); $count = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE type = 'feed_item'")); $this->assertEqual($count, 100, 'Imported 100 nodes.'); - $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_node_item}")); - $this->assertEqual($count, 100, 'Found 100 records in feeds_node_item.'); + $count = db_result(db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")); + $this->assertEqual($count, 100, 'Found 100 records in feeds_item.'); } } diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc index 5f2a8d2bb8133326c2f4a1f048928f7935145034..d3c5debd489e812a9b80348ffe6130a7ff88e681 100644 --- a/plugins/FeedsNodeProcessor.inc +++ b/plugins/FeedsNodeProcessor.inc @@ -19,6 +19,14 @@ define('FEEDS_NODE_DEFAULT_FORMAT', -1); * Creates nodes from feed items. */ class FeedsNodeProcessor extends FeedsProcessor { + /** + * Define entity type. + */ + protected function __construct($id) { + parent::__construct($id); + $this->entity_type = 'node'; + } + /** * Implements FeedsProcessor::process(). */ @@ -37,8 +45,7 @@ class FeedsNodeProcessor extends FeedsProcessor { // Assemble node, map item to it, save. try { - $node = $this->buildNode($nid, $source->feed_nid); - $node->feeds_node_item->hash = $hash; + $node = empty($nid) ? $this->newNode($source, $hash) : $this->loadNode($source, $hash, $nid); $this->map($source, $parser_result, $node); node_save($node); if (!empty($nid)) { @@ -76,17 +83,17 @@ class FeedsNodeProcessor extends FeedsProcessor { public function clear(FeedsSource $source) { $state = $source->state(FEEDS_PROCESS_CLEAR); if (empty($state->total)) { - $state->total = db_query("SELECT COUNT(n.nid) FROM {node} n JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE fn.id = :id AND fn.feed_nid = :nid", array(':id' => $source->id, ':nid' => $source->feed_nid))->fetchField(); + $state->total = db_query("SELECT COUNT(n.nid) FROM {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE fi.id = :id AND fi.feed_nid = :nid", array(':id' => $source->id, ':nid' => $source->feed_nid))->fetchField(); } $count = $this->getLimit(); $nids = array(); - $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE fn.id = :id AND fn.feed_nid = :nid", 0, $count, array(':id' => $source->id, ':nid' => $source->feed_nid)); + $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE fi.id = :id AND fi.feed_nid = :nid", 0, $count, array(':id' => $source->id, ':nid' => $source->feed_nid)); foreach ($nodes as $node) { $nids[$node->nid] = $node->nid; $state->deleted++; } node_delete_multiple($nids); - if (db_query_range("SELECT 1 FROM {node} n JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE fn.id = :id AND fn.feed_nid = :nid", 0, 1, array(':id' => $source->id, ':nid' => $source->feed_nid))->fetchField()) { + if (db_query_range("SELECT 1 FROM {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE fi.id = :id AND fi.feed_nid = :nid", 0, 1, array(':id' => $source->id, ':nid' => $source->feed_nid))->fetchField()) { $state->progress($state->total, $state->deleted); return; } @@ -122,13 +129,13 @@ class FeedsNodeProcessor extends FeedsProcessor { return; } $count = $this->getLimit(); - $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fni ON n.nid = fni.nid WHERE fni.id = :id AND n.created < :created", 0, $count, array(':id' => $this->id, ':created' => REQUEST_TIME - $time)); + $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE fi.id = :id AND n.created < :created", 0, $count, array(':id' => $this->id, ':created' => REQUEST_TIME - $time)); $nids = array(); foreach ($nodes as $node) { $nids[$node->nid] = $node->nid; } node_delete_multiple($nids); - if (db_query_range("SELECT 1 FROM {node} n JOIN {feeds_node_item} fni ON n.nid = fni.nid WHERE fni.id = :id AND n.created < :created", 0, 1, array(':id' => $this->id, ':created' => REQUEST_TIME - $time))->fetchField()) { + if (db_query_range("SELECT 1 FROM {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE fi.id = :id AND n.created < :created", 0, 1, array(':id' => $this->id, ':created' => REQUEST_TIME - $time))->fetchField()) { return FEEDS_BATCH_ACTIVE; } return FEEDS_BATCH_COMPLETE; @@ -141,13 +148,6 @@ class FeedsNodeProcessor extends FeedsProcessor { return $this->config['expire']; } - /** - * Return a count for the given items - */ - public function itemCount(FeedsSource $source) { - return db_query("SELECT count(*) FROM {feeds_node_item} WHERE feed_nid = :feed_nid", array(':feed_nid' => $source->feed_nid))->fetchField(); - } - /** * Override parent::configDefaults(). */ @@ -248,16 +248,6 @@ class FeedsNodeProcessor extends FeedsProcessor { */ public function setTargetElement($target_node, $target_element, $value) { switch ($target_element) { - case 'url': - case 'guid': - $target_node->feeds_node_item->$target_element = $value; - break; - case 'title': - case 'status': - case 'nid': - case 'uid': - $target_node->$target_element = $value; - break; case 'created': $target_node->created = REQUEST_TIME; if (is_numeric($value)) { @@ -271,6 +261,9 @@ class FeedsNodeProcessor extends FeedsProcessor { $target_node->created = $value->getValue(); } break; + default: + parent::setTargetElement($target_node, $target_element, $value); + break; } } @@ -279,7 +272,7 @@ class FeedsNodeProcessor extends FeedsProcessor { */ public function getMappingTargets() { $type = node_type_get_type($this->config['content_type']); - $targets = array(); + $targets = parent::getMappingTargets(); if ($type->has_title) { $targets['title'] = array( 'name' => t('Title'), @@ -304,16 +297,6 @@ class FeedsNodeProcessor extends FeedsProcessor { 'name' => t('Published date'), 'description' => t('The UNIX time when a node has been published.'), ), - 'url' => array( - 'name' => t('URL'), - 'description' => t('The external URL of the node. E. g. the feed item URL in the case of a syndication feed. May be unique.'), - 'optional_unique' => TRUE, - ), - 'guid' => array( - 'name' => t('GUID'), - 'description' => t('The external GUID of the node. E. g. the feed item GUID in the case of a syndication feed. May be unique.'), - 'optional_unique' => TRUE, - ), ); // Let other modules expose mapping targets. @@ -327,6 +310,9 @@ class FeedsNodeProcessor extends FeedsProcessor { * Get nid of an existing feed item node if available. */ protected function existingItemId(FeedsSource $source, FeedsParserResult $result) { + if ($nid = parent::existingItemId($source, $result)) { + return $nid; + } // Iterate through all unique targets and test whether they do already // exist in the database. @@ -335,12 +321,6 @@ class FeedsNodeProcessor extends FeedsProcessor { case 'nid': $nid = db_query("SELECT nid FROM {node} WHERE nid = :nid", array(':nid' => $value))->fetchField(); break; - case 'url': - $nid = db_query("SELECT nid FROM {feeds_node_item} WHERE feed_nid = :nid AND id = :id AND url = :url", array(':nid' => $source->feed_nid, ':id' => $source->id, ':url' => $value))->fetchField(); - break; - case 'guid': - $nid = db_query("SELECT nid FROM {feeds_node_item} WHERE feed_nid = :nid AND id = :id AND guid = :guid", array(':nid' => $source->feed_nid, ':id' => $source->id, ':guid' => $value))->fetchField(); - break; } if ($nid) { // Return with the first nid found. @@ -351,77 +331,48 @@ class FeedsNodeProcessor extends FeedsProcessor { } /** - * Creates a new node object in memory and returns it. + * Creates a new node in memory and returns it. */ - protected function buildNode($nid, $feed_nid) { + protected function newNode($source, $hash) { $node = new stdClass(); - $populate = FALSE; - if (empty($nid)) { - $node->created = REQUEST_TIME; - $populate = TRUE; - } - else { - if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { - $node = node_load($nid, NULL, TRUE); - } - else { - $node = db_query("SELECT nid, vid, created FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetch(); - $populate = TRUE; - } - } - if ($populate) { - $node->type = $this->config['content_type']; - $node->changed = REQUEST_TIME; - $node->format = ($this->config['input_format'] == FEEDS_NODE_DEFAULT_FORMAT) ? filter_fallback_format() : $this->config['input_format']; - $node->feeds_node_item = new stdClass(); - $node->feeds_node_item->id = $this->id; - $node->feeds_node_item->imported = REQUEST_TIME; - $node->feeds_node_item->feed_nid = $feed_nid; - $node->feeds_node_item->url = ''; - $node->feeds_node_item->guid = ''; - } - - // Give mappers a hint at what they're operating on. - $node->entity_type = 'node'; - - // Let other modules populate default values. + $node->type = $this->config['content_type']; + $node->changed = REQUEST_TIME; + $node->format = ($this->config['input_format'] == FEEDS_NODE_DEFAULT_FORMAT) ? filter_fallback_format() : $this->config['input_format']; + $node->created = REQUEST_TIME; + $this->newItemInfo($node, $source->feed_nid, $hash); node_object_prepare($node); - // Populate properties that are set by node_object_prepare(). - $node->log = 'Created/updated by FeedsNodeProcessor'; - if ($populate) { - $node->uid = $this->config['author']; - } + $node->log = 'Created by FeedsNodeProcessor'; + $node->uid = $this->config['author']; return $node; } /** - * Create MD5 hash of item and mappings array. - * - * Include mappings as a change in mappings may have an affect on the item - * produced. + * Loads an existing node. * - * @return Always returns a hash, even with empty, NULL, FALSE: - * Empty arrays return 40cd750bba9870f18aada2478b24840a - * Empty/NULL/FALSE strings return d41d8cd98f00b204e9800998ecf8427e + * If the update existing method is not FEEDS_UPDATE_EXISTING, only the node + * table will be loaded, foregoing the node_load API for better performance. */ - protected function hash($item) { - static $serialized_mappings; - if (!$serialized_mappings) { - $serialized_mappings = serialize($this->config['mappings']); + protected function loadNode($source, $hash, $nid) { + if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { + $node = node_load($nid, NULL, TRUE); } - return hash('md5', serialize($item) . $serialized_mappings); - } - - /** - * Retrieve MD5 hash of $nid from DB. - * @return Empty string if no item is found, hash otherwise. - */ - protected function getHash($nid) { - if ($hash = db_query("SELECT hash FROM {feeds_node_item} WHERE nid = :nid", array(':nid' => $nid))->fetchField()) { - // Return with the hash. - return $hash; + else { + // We're replacing the existing node. Only save the absolutely necessary. + $node = db_query("SELECT created, nid, vid, type FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetch(); + $node->uid = $this->config['author']; + } + if ($this->loadItemInfo($node)) { + $this->newItemInfo($node, $source->feed_nid, $hash); + } + node_object_prepare($node); + // Populate properties that are set by node_object_prepare(). + if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { + $node->log = 'Updated by FeedsNodeProcessor'; } - return ''; + else { + $node->log = 'Replaced by FeedsNodeProcessor'; + } + return $node; } } diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc index 24fb078902d1388dd83a67321f0d1ab478bae5ab..b30678e19b17ceb3e58f001a643a554f63bf3fcb 100644 --- a/plugins/FeedsProcessor.inc +++ b/plugins/FeedsProcessor.inc @@ -15,6 +15,10 @@ define('FEEDS_PROCESS_LIMIT', 50); * Abstract class, defines interface for processors. */ abstract class FeedsProcessor extends FeedsPlugin { + /** + * Entity type this processor operates on. + */ + protected $entity_type; /** * Process the result of the parser or previous processors. @@ -77,10 +81,10 @@ abstract class FeedsProcessor extends FeedsPlugin { } /** - * Return the number of items imported by this processor. + * Counts the number of items imported by this processor. */ public function itemCount(FeedsSource $source) { - return 0; + return db_query("SELECT count(*) FROM {feeds_item} WHERE entity_type = :entity_type AND feed_nid = :feed_nid", array(':entity_type' => $this->entity_type, ':feed_nid' => $source->feed_nid))->fetchField(); } /** @@ -203,7 +207,18 @@ abstract class FeedsProcessor extends FeedsPlugin { * FALSE otherwise. */ public function getMappingTargets() { - return array(); + return array( + 'url' => array( + 'name' => t('URL'), + 'description' => t('The external URL of the item. E. g. the feed item URL in the case of a syndication feed. May be unique.'), + 'optional_unique' => TRUE, + ), + 'guid' => array( + 'name' => t('GUID'), + 'description' => t('The globally unique identifier of the item. E. g. the feed item GUID in the case of a syndication feed. May be unique.'), + 'optional_unique' => TRUE, + ), + ); } /** @@ -212,7 +227,15 @@ abstract class FeedsProcessor extends FeedsPlugin { * @ingroup mappingapi */ public function setTargetElement($target_item, $target_element, $value) { - $target_item->$target_element = $value; + switch ($target_element) { + case 'url': + case 'guid': + $target_item->feeds_item->$target_element = $value; + break; + default: + $target_item->$target_element = $value; + break; + } } /** @@ -224,11 +247,37 @@ abstract class FeedsProcessor extends FeedsPlugin { * The source information about this import. * @param $result * A FeedsParserResult object. + * + * @return + * The serial id of an entity if found, 0 otherwise. */ protected function existingItemId(FeedsSource $source, FeedsParserResult $result) { + $query = db_select('feeds_item') + ->fields('feeds_item', array('entity_id')) + ->condition('feed_nid', $source->feed_nid) + ->condition('entity_type', $this->entity_type) + ->condition('id', $source->id); + + // Iterate through all unique targets and test whether they do already + // exist in the database. + foreach ($this->uniqueTargets($source, $result) as $target => $value) { + switch ($target) { + case 'url': + $entity_id = $query->condition('url', $value)->execute()->fetchField(); + break; + case 'guid': + $entity_id = $query->condition('guid', $value)->execute()->fetchField(); + break; + } + if (isset($entity_id)) { + // Return with the content id found. + return $entity_id; + } + } return 0; } + /** * Utility function that iterates over a target array and retrieves all * sources that are unique. @@ -252,4 +301,75 @@ abstract class FeedsProcessor extends FeedsPlugin { } return $targets; } + + /** + * Adds Feeds specific information on $entity->feeds_item. + * + * @param $entity + * The entity object to be populated with new item info. + * @param $feed_nid + * The feed nid of the source that produces this entity. + * @param $hash + * The fingerprint of the source item. + */ + protected function newItemInfo($entity, $feed_nid, $hash = '') { + $entity->feeds_item = new stdClass(); + $entity->feeds_item->entity_id = 0; + $entity->feeds_item->entity_type = $this->entity_type; + $entity->feeds_item->id = $this->id; + $entity->feeds_item->feed_nid = $feed_nid; + $entity->feeds_item->imported = REQUEST_TIME; + $entity->feeds_item->hash = $hash; + $entity->feeds_item->url = ''; + $entity->feeds_item->guid = ''; + } + + /** + * Loads existing entity information and places it on $entity->feeds_item. + * + * @param $entity + * The entity object to load item info for. Id key must be present. + * + * @return + * TRUE if item info could be loaded, false if not. + */ + protected function loadItemInfo($entity) { + $entity_info = entity_get_info($this->entity_type); + $key = $entity_info['entity keys']['id']; + if ($item_info = feeds_item_info_load($this->entity_type, $entity->$key)) { + $entity->feeds_item = $item_info; + return TRUE; + } + return FALSE; + } + + /** + * Create MD5 hash of item and mappings array. + * + * Include mappings as a change in mappings may have an affect on the item + * produced. + * + * @return Always returns a hash, even with empty, NULL, FALSE: + * Empty arrays return 40cd750bba9870f18aada2478b24840a + * Empty/NULL/FALSE strings return d41d8cd98f00b204e9800998ecf8427e + */ + protected function hash($item) { + static $serialized_mappings; + if (!$serialized_mappings) { + $serialized_mappings = serialize($this->config['mappings']); + } + return hash('md5', serialize($item) . $serialized_mappings); + } + + /** + * Retrieve MD5 hash of $nid from DB. + * @return Empty string if no item is found, hash otherwise. + */ + protected function getHash($entity_id) { + if ($hash = db_query("SELECT hash FROM {feeds_item} WHERE entity_type = :type AND entity_id = :id", array(':type' => $this->entity_type, ':id' => $entity_id))->fetchField()) { + // Return with the hash. + return $hash; + } + return ''; + } } diff --git a/plugins/FeedsTermProcessor.inc b/plugins/FeedsTermProcessor.inc index 9cf4fb8768143221735fc5e48e9cf9e16386ba1b..cd817a53c99ba91c9d1383c0b9981d72a9a532c3 100644 --- a/plugins/FeedsTermProcessor.inc +++ b/plugins/FeedsTermProcessor.inc @@ -10,6 +10,14 @@ * Feeds processor plugin. Create taxonomy terms from feed items. */ class FeedsTermProcessor extends FeedsProcessor { + /** + * Define entity type. + */ + protected function __construct($id) { + parent::__construct($id); + $this->entity_type = 'taxonomy_term'; + } + /** * Implements FeedsProcessor::process(). @@ -28,11 +36,12 @@ class FeedsTermProcessor extends FeedsProcessor { if (!($tid = $this->existingItemId($source, $parser_result)) || $this->config['update_existing'] != FEEDS_SKIP_EXISTING) { // Map item to a term. - $term = new stdClass(); if ($tid && $this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { - $term = taxonomy_term_load($tid); + $term = $this->loadTerm($source, $tid); + } + else { + $term = $this->newTerm($source); } - $term->entity_type = 'taxonomy_term'; $term = $this->map($source, $parser_result, $term); // Check if term name is set, otherwise continue. @@ -91,10 +100,12 @@ class FeedsTermProcessor extends FeedsProcessor { $vocabulary = $this->vocabulary(); $terms = db_query("SELECT td.tid FROM {taxonomy_term_data} td - JOIN {feeds_term_item} ft ON td.tid = ft.tid + JOIN {feeds_item} fi + ON fi.entity_type = 'taxonomy_term' + AND td.tid = fi.entity_id WHERE td.vid = :vid - AND ft.id = :id - AND ft.feed_nid = :feed_nid", + AND fi.id = :id + AND fi.feed_nid = :feed_nid", array( ':vid' => $vocabulary->vid, ':id' => $this->id, @@ -114,22 +125,6 @@ class FeedsTermProcessor extends FeedsProcessor { } } - /** - * Execute mapping on an item. - */ - protected function map(FeedsSource $source, FeedsParserResult $result, $target_term = NULL) { - // Prepare term object, have parent class do the iterating. - if (!$target_term) { - $target_term = new stdClass(); - } - if (!$vocabulary = $this->vocabulary()) { - throw new Exception(t('No vocabulary specified for term processor')); - } - $target_term->vid = $vocabulary->vid; - $target_term = parent::map($source, $result, $target_term); - return $target_term; - } - /** * Override parent::configDefaults(). */ @@ -184,7 +179,8 @@ class FeedsTermProcessor extends FeedsProcessor { * Return available mapping targets. */ public function getMappingTargets() { - $targets = array( + $targets = parent::getMappingTargets(); + $targets += array( 'name' => array( 'name' => t('Term name'), 'description' => t('Name of the taxonomy term.'), @@ -207,6 +203,9 @@ class FeedsTermProcessor extends FeedsProcessor { * Get id of an existing feed item term if available. */ protected function existingItemId(FeedsSource $source, FeedsParserResult $result) { + if ($tid = parent::existingItemId($source, $result)) { + return $tid; + } // The only possible unique target is name. foreach ($this->uniqueTargets($source, $result) as $target => $value) { @@ -243,4 +242,26 @@ class FeedsTermProcessor extends FeedsProcessor { } } } + + /** + * Creates a new term in memory and returns it. + */ + protected function newTerm($source) { + $term = new stdClass(); + $this->newItemInfo($term, $source->feed_nid); + $vocabulary = $this->vocabulary(); + $term->vid = $vocabulary->vid; + return $term; + } + + /** + * Loads an existing term. + */ + protected function loadTerm($source, $tid) { + $term = taxonomy_term_load($tid); + if ($this->loadItemInfo($term)) { + $this->newItemInfo($term, $source->feed_nid); + } + return $term; + } } diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc index f2a010cf94d6c3ad222b01631b967645f8e47a0f..2b7c44078c0d2aee3421dc8a7b635895ae1dabde 100644 --- a/plugins/FeedsUserProcessor.inc +++ b/plugins/FeedsUserProcessor.inc @@ -10,6 +10,14 @@ * Feeds processor plugin. Create users from feed items. */ class FeedsUserProcessor extends FeedsProcessor { + /** + * Define entity type. + */ + protected function __construct($id) { + parent::__construct($id); + $this->entity_type = 'user'; + } + /** * Implements FeedsProcessor::process(). @@ -23,8 +31,14 @@ class FeedsUserProcessor extends FeedsProcessor { if (!($uid = $this->existingItemId($source, $parser_result)) || $this->config['update_existing']) { - // Map item to a term. - $account = $this->map($source, $parser_result); + // Load / create new user and execute mapping. + if (empty($uid)) { + $account = $this->newUser($source); + } + else { + $account = $this->loadUser($source, $uid); + } + $account = $this->map($source, $parser_result, $account); // Check if user name and mail are set, otherwise continue. if (empty($account->name) || empty($account->mail) || !valid_email_address($account->mail)) { @@ -32,11 +46,6 @@ class FeedsUserProcessor extends FeedsProcessor { continue; } - // Add term id if available. - if (!empty($uid)) { - $account->uid = $uid; - } - // Save the user. user_save($account, (array) $account); if ($account->uid && $account->openid) { @@ -91,21 +100,6 @@ class FeedsUserProcessor extends FeedsProcessor { throw new Exception(t('User processor does not support deleting users.')); } - /** - * Execute mapping on an item. - */ - protected function map(FeedsSource $source, FeedsParserResult $result) { - // Prepare term object. - $target_account = new stdClass(); - $target_account->uid = 0; - $target_account->entity_type = 'user'; - $target_account->roles = array_filter($this->config['roles']); - $target_account->status = $this->config['status']; - - // Have parent class do the iterating. - return parent::map($source, $result, $target_account); - } - /** * Override parent::configDefaults(). */ @@ -153,18 +147,12 @@ class FeedsUserProcessor extends FeedsProcessor { return $form; } - /** - * Set target element. - */ - public function setTargetElement(&$target_item, $target_element, $value) { - $target_item->$target_element = $value; - } - /** * Return available mapping targets. */ public function getMappingTargets() { - $targets = array( + $targets = parent::getMappingTargets(); + $targets += array( 'name' => array( 'name' => t('User name'), 'description' => t('Name of the user.'), @@ -199,6 +187,9 @@ class FeedsUserProcessor extends FeedsProcessor { * Get id of an existing feed item term if available. */ protected function existingItemId(FeedsSource $source, FeedsParserResult $result) { + if ($uid = parent::existingItemId($source, $result)) { + return $uid; + } // Iterate through all unique targets and try to find a user for the // target's value. @@ -221,4 +212,27 @@ class FeedsUserProcessor extends FeedsProcessor { } return 0; } + + /** + * Creates a new user account in memory and returns it. + */ + protected function newUser($source) { + $account = new stdClass(); + $account->uid = 0; + $account->roles = array_filter($this->config['roles']); + $account->status = $this->config['status']; + $this->newItemInfo($account, $source->feed_nid); + return $account; + } + + /** + * Loads an existing user. + */ + protected function loadUser($source, $uid) { + $account = user_load($uid); + if (!$this->loadItemInfo($account)) { + $this->newItemInfo($account, $source->feed_nid); + } + return $account; + } } diff --git a/tests/feeds_parser_sitemap.test b/tests/feeds_parser_sitemap.test index 449fefafd2092ea7eac3bcba6ebbd77ed653af96..7d16677e5f06099efe6c15ee9fd48626b34023e7 100644 --- a/tests/feeds_parser_sitemap.test +++ b/tests/feeds_parser_sitemap.test @@ -83,11 +83,11 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertText('Created 5 Article nodes.'); // Assert DB status. - $count = db_query("SELECT COUNT(*) FROM {feeds_node_item}")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")->fetchField(); $this->assertEqual($count, 5, 'Accurate number of items in database.'); // Check items against known content of feed. - $items = db_query('SELECT * FROM {feeds_node_item} WHERE feed_nid = :nid ORDER BY nid', array(':nid' => $nid)); + $items = db_query("SELECT * FROM {feeds_item} WHERE entity_type = 'node' AND feed_nid = :nid ORDER BY nid", array(':nid' => $nid)); // Check first item. date_default_timezone_set('GMT'); @@ -96,8 +96,9 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertEqual($node->title, 'monthly', 'Feed item 1 changefreq is correct.'); $this->assertEqual($node->body, '0.8', 'Feed item 1 priority is correct.'); $this->assertEqual($node->created, strtotime('2005-01-01'), 'Feed item 1 lastmod is correct.'); - $this->assertEqual($node->feeds_node_item->url, 'http://www.example.com/', 'Feed item 1 url is correct.'); - $this->assertEqual($node->feeds_node_item->url, $node->feeds_node_item->guid, 'Feed item 1 guid is correct.'); + $info = feeds_item_info_load('node', $node->nid); + $this->assertEqual($info->url, 'http://www.example.com/', 'Feed item 1 url is correct.'); + $this->assertEqual($info->url, $info->guid, 'Feed item 1 guid is correct.'); // Check second item. $item = $items->fetch(); @@ -105,8 +106,9 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertEqual($node->title, 'weekly', 'Feed item 2 changefreq is correct.'); $this->assertEqual($node->body, '', 'Feed item 2 priority is correct.'); // $node->created is... recently - $this->assertEqual($node->feeds_node_item->url, 'http://www.example.com/catalog?item=12&desc=vacation_hawaii', 'Feed item 2 url is correct.'); - $this->assertEqual($node->feeds_node_item->url, $node->feeds_node_item->guid, 'Feed item 2 guid is correct.'); + $info = feeds_item_info_load('node', $node->nid); + $this->assertEqual($info->url, 'http://www.example.com/catalog?item=12&desc=vacation_hawaii', 'Feed item 2 url is correct.'); + $this->assertEqual($info->url, $info->guid, 'Feed item 2 guid is correct.'); // Check third item. $item = $items->fetch(); @@ -114,8 +116,9 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertEqual($node->title, 'weekly', 'Feed item 3 changefreq is correct.'); $this->assertEqual($node->body, '', 'Feed item 3 priority is correct.'); $this->assertEqual($node->created, strtotime('2004-12-23'), 'Feed item 3 lastmod is correct.'); - $this->assertEqual($node->feeds_node_item->url, 'http://www.example.com/catalog?item=73&desc=vacation_new_zealand', 'Feed item 3 url is correct.'); - $this->assertEqual($node->feeds_node_item->url, $node->feeds_node_item->guid, 'Feed item 3 guid is correct.'); + $info = feeds_item_info_load('node', $node->nid); + $this->assertEqual($info->url, 'http://www.example.com/catalog?item=73&desc=vacation_new_zealand', 'Feed item 3 url is correct.'); + $this->assertEqual($info->url, $info->guid, 'Feed item 3 guid is correct.'); // Check fourth item. $item = $items->fetch(); @@ -123,8 +126,9 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertEqual($node->title, '', 'Feed item 4 changefreq is correct.'); $this->assertEqual($node->body, '0.3', 'Feed item 4 priority is correct.'); $this->assertEqual($node->created, strtotime('2004-12-23T18:00:15+00:00'), 'Feed item 4 lastmod is correct.'); - $this->assertEqual($node->feeds_node_item->url, 'http://www.example.com/catalog?item=74&desc=vacation_newfoundland', 'Feed item 4 url is correct.'); - $this->assertEqual($node->feeds_node_item->url, $node->feeds_node_item->guid, 'Feed item 1 guid is correct.'); + $info = feeds_item_info_load('node', $node->nid); + $this->assertEqual($info->url, 'http://www.example.com/catalog?item=74&desc=vacation_newfoundland', 'Feed item 4 url is correct.'); + $this->assertEqual($info->url, $info->guid, 'Feed item 1 guid is correct.'); // Check fifth item. $item = $items->fetch(); @@ -132,8 +136,9 @@ class FeedsSitemapParserTestCase extends FeedsWebTestCase { $this->assertEqual($node->title, '', 'Feed item 5 changefreq is correct.'); $this->assertEqual($node->body, '', 'Feed item 5 priority is correct.'); $this->assertEqual($node->created, strtotime('2004-11-23'), 'Feed item 5 lastmod is correct.'); - $this->assertEqual($node->feeds_node_item->url, 'http://www.example.com/catalog?item=83&desc=vacation_usa', 'Feed item 5 url is correct.'); - $this->assertEqual($node->feeds_node_item->url, $node->feeds_node_item->guid, 'Feed item 5 guid is correct.'); + $info = feeds_item_info_load('node', $node->nid); + $this->assertEqual($info->url, 'http://www.example.com/catalog?item=83&desc=vacation_usa', 'Feed item 5 url is correct.'); + $this->assertEqual($info->url, $info->guid, 'Feed item 5 guid is correct.'); // Check for more items. $item = $items->fetch(); diff --git a/tests/feeds_processor_node.test b/tests/feeds_processor_node.test index e806cbabbd3631256f656a7df8efed3b368a0d91..625b41723697a01ee7353f4221d6acaf5ee7d5a0 100644 --- a/tests/feeds_processor_node.test +++ b/tests/feeds_processor_node.test @@ -89,7 +89,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->drupalGet('node/'. $article_nid); $this->assertNoRaw('node/'. $article_nid .'/import'); $this->assertNoRaw('node/'. $article_nid .'/delete-items'); - $this->assertEqual("Created/updated by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revision} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField()); + $this->assertEqual("Created by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revision} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField()); // Assert accuracy of aggregated information. $this->drupalGet('node'); @@ -97,7 +97,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->assertDevseedFeedContent(); // Assert DB status. - $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id")->fetchField(); $this->assertEqual($count, 10, 'Accurate number of items in database.'); // Assert default input format on first imported feed node. @@ -112,12 +112,12 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->assertText('There is no new content.'); // Assert DB status, there still shouldn't be more than 10 items. - $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id")->fetchField(); $this->assertEqual($count, 10, 'Accurate number of items in database.'); // All of the above tests should have produced published nodes, set default // to unpublished, import again. - $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE n.status = 1")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE n.status = 1")->fetchField(); $this->assertEqual($count, 10, 'All items are published.'); $edit = array( 'node_options[status]' => FALSE, @@ -125,7 +125,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type')); $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); - $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE n.status = 0")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id WHERE n.status = 0")->fetchField(); $this->assertEqual($count, 10, 'No items are published.'); $edit = array( 'node_options[status]' => TRUE, @@ -171,7 +171,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { // Assert author. $this->drupalGet('node'); $this->assertPattern('/<span class="username">' . check_plain($this->auth_user->name) . '<\/span>/'); - $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {feeds_item} fi JOIN {node} n ON fi.entity_type = 'node' AND fi.entity_id = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField(); $this->assertEqual($count, 10, 'Accurate number of items in database.'); // Assert input format. @@ -183,11 +183,11 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { // Set to update existing, remove authorship of above nodes and import again. $this->setSettings('syndication', 'FeedsNodeProcessor', array('update_existing' => 2)); - db_query("UPDATE {node} n JOIN {feeds_node_item} fi ON n.nid = fi.nid SET n.uid = 0, fi.hash=''"); + db_query("UPDATE {node} n JOIN {feeds_item} fi ON fi.entity_type = 'node' AND n.nid = fi.entity_id SET n.uid = 0, fi.hash=''"); $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); $this->drupalGet('node'); $this->assertNoPattern('/<span class="username">' . check_plain($this->auth_user->name) . '<\/span>/'); - $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {feeds_item} fi JOIN {node} n ON fi.entity_type = 'node' AND fi.entity_id = n.nid WHERE n.uid = :uid", array(':uid' => $this->auth_user->uid))->fetchField(); $this->assertEqual($count, 0, 'Accurate number of items in database.'); // Map feed node's author to feed item author, update - feed node's items @@ -327,11 +327,11 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { } /** - * Check that the total number of entries in the feeds_node_item table is correct. + * Check that the total number of entries in the feeds_item table is correct. */ function assertFeedItemCount($num) { // Assert DB status, there should be 10 again. - $count = db_query("SELECT COUNT(*) FROM {feeds_node_item}")->fetchField(); + $count = db_query("SELECT COUNT(*) FROM {feeds_item} WHERE entity_type = 'node'")->fetchField(); $this->assertEqual($count, $num, 'Accurate number of items in database.'); } diff --git a/views/feeds.views.inc b/views/feeds.views.inc index 2fc30e9c0281ffc31ab9a5e0ed6ffc35bb6779a9..0f321be3884c5527620dc75959a87bab1fec7ddb 100644 --- a/views/feeds.views.inc +++ b/views/feeds.views.inc @@ -67,19 +67,12 @@ function feeds_views_data() { /** * Expose feeds_node_item table to views. */ - $data['feeds_node_item']['table'] = array( - 'group' => 'Feeds Item', - 'join' => array( - 'node' => array( - 'left_field' => 'nid', - 'field' => 'nid', - 'type' => 'LEFT', - ), - ), + $data['feeds_item']['table'] = array( + 'group' => 'Feeds item', ); - $data['feeds_node_item']['feed_nid'] = array( + $data['feeds_item']['feed_nid'] = array( 'title' => t('Owner feed nid'), - 'help' => t('The node id of the owner feed if available.'), + 'help' => t('The node id of the owner feed node if available.'), 'field' => array( 'handler' => 'views_handler_field_numeric', 'click sortable' => TRUE, @@ -100,13 +93,13 @@ function feeds_views_data() { ), 'relationship' => array( 'title' => t('Owner feed'), - 'help' => t('Relate a node to its owner feed node if available.'), + 'help' => t('Relate a feed item to its owner feed node if available.'), 'label' => t('Owner feed'), 'base' => 'node', 'base field' => 'nid', ), ); - $data['feeds_node_item']['url'] = array( + $data['feeds_item']['url'] = array( 'title' => t('Item URL'), 'help' => t('Contains the URL of the feed item.'), 'field' => array( @@ -128,7 +121,7 @@ function feeds_views_data() { 'help' => t('Sort on a Feeds Item\'s URL field.'), ), ); - $data['feeds_node_item']['guid'] = array( + $data['feeds_item']['guid'] = array( 'title' => t('Item GUID'), 'help' => t('Contains the GUID of the feed item.'), 'field' => array( @@ -149,7 +142,7 @@ function feeds_views_data() { 'help' => t('Sort on a Feeds Item\'s GUID field.'), ), ); - $data['feeds_node_item']['imported'] = array( + $data['feeds_item']['imported'] = array( 'title' => t('Import date'), 'help' => t('Contains the import date of the feed item.'), 'field' => array( @@ -171,6 +164,23 @@ function feeds_views_data() { 'help' => t('Argument on a Feeds Item\'s import date field.'), ), ); + + foreach (array('node', 'taxonomy_term', 'user') as $entity_type) { + $info = entity_get_info($entity_type); + $data['feeds_item']['table']['join'][$info['base table']] = array( + 'left_field' => $info['entity keys']['id'], + 'field' => 'entity_id', + 'type' => 'LEFT', + 'extra' => array( + array( + 'table' => 'feeds_item', + 'field' => 'entity_type', + 'value' => $entity_type, + 'operator' => '=', + ), + ), + ); + } return $data; }