Index: feeds.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.install,v retrieving revision 1.13.2.1 diff -u -p -r1.13.2.1 feeds.install --- feeds.install 25 Sep 2010 16:07:50 -0000 1.13.2.1 +++ feeds.install 5 Nov 2010 00:44:03 -0000 @@ -130,6 +130,12 @@ function feeds_schema() { 'default' => '', 'description' => t('The hash of the item.'), ), + 'touched' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Mark when a feed item has last been touched, even if not updated.'), + ), ), 'primary key' => array('nid'), 'indexes' => array( @@ -567,3 +573,20 @@ function feeds_update_6013() { variable_set('feeds_reschedule', TRUE); return array(); } + +/** + * Add touched column to feeds_node_item. + */ +function feeds_update_60014() { + $ret = array(); + + $spec = array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Mark when a feed item has last been touched, even if not updated.'), + ); + + db_add_field($ret, 'feeds_node_item', 'touched', $spec); + return $ret; +} \ No newline at end of file Index: includes/FeedsBatch.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/includes/FeedsBatch.inc,v retrieving revision 1.15.2.1 diff -u -p -r1.15.2.1 FeedsBatch.inc --- includes/FeedsBatch.inc 25 Oct 2010 22:43:03 -0000 1.15.2.1 +++ includes/FeedsBatch.inc 5 Nov 2010 00:44:03 -0000 @@ -150,6 +150,8 @@ class FeedsImportBatch extends FeedsBatc public $feed_nid; public $created; public $updated; + public $unpublished; + public $deleted; public function __construct($raw = '', $feed_nid = 0) { parent::__construct(); Index: plugins/FeedsNodeProcessor.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsNodeProcessor.inc,v retrieving revision 1.51.2.2 diff -u -p -r1.51.2.2 FeedsNodeProcessor.inc --- plugins/FeedsNodeProcessor.inc 25 Oct 2010 22:43:03 -0000 1.51.2.2 +++ plugins/FeedsNodeProcessor.inc 5 Nov 2010 00:44:04 -0000 @@ -15,6 +15,10 @@ define('FEEDS_NODE_SKIP_EXISTING', 0); define('FEEDS_NODE_REPLACE_EXISTING', 1); define('FEEDS_NODE_UPDATE_EXISTING', 2); +define('FEEDS_SYNC_REMOVED_IGNORE', 0); +define('FEEDS_SYNC_REMOVED_UNPUBLISH', 1); +define('FEEDS_SYNC_REMOVED_DELETE', 3); + /** * Creates nodes from feed items. */ @@ -32,12 +36,21 @@ class FeedsNodeProcessor extends FeedsPr } while ($item = $batch->shiftItem()) { + // TODO: There might be an existing indicator of when batch processing started? + if (!isset($batch_started)) $batch_started = FEEDS_REQUEST_TIME; // Create/update if item does not exist or update existing is enabled. if (!($nid = $this->existingItemId($batch, $source)) || ($this->config['update_existing'] != FEEDS_SKIP_EXISTING)) { // Only proceed if item has actually changed. $hash = $this->hash($item); if (!empty($nid) && $hash == $this->getHash($nid)) { + // To be able to take action on items that have been removed, just indicate that this was still in the feed. + db_query("UPDATE {feeds_node_item} SET touched = %d WHERE nid = %d", FEEDS_REQUEST_TIME, $nid); + + // If this item was previously removed, but has now appeared again, make sure the corresponding node is published. + // TODO: There probably should be a message here for what just happened. + db_query("UPDATE {node} SET status = 1 WHERE nid = %d", $nid); + continue; } @@ -68,6 +81,11 @@ class FeedsNodeProcessor extends FeedsPr } } + // Handle nodes whose corresponding feed items have been removed. + if ($this->config['sync'] != FEEDS_SYNC_REMOVED_IGNORE) { + $this->syncNodes($batch, $source, $batch_started); + } + // 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' => node_get_types('name', $this->config['content_type'])))); @@ -75,8 +93,14 @@ class FeedsNodeProcessor extends FeedsPr elseif ($batch->updated) { drupal_set_message(format_plural($batch->updated, 'Updated @number @type node.', 'Updated @number @type nodes.', array('@number' => $batch->updated, '@type' => node_get_types('name', $this->config['content_type'])))); } + elseif ($batch->unpublished) { + drupal_set_message(format_plural($batch->unpublished, 'Unpublished @number @type node.', 'Unpublished @number @type nodes.', array('@number' => $batch->unpublished, '@type' => node_get_types('name', $this->config['content_type'])))); + } + elseif ($batch->deleted) { + drupal_set_message(format_plural($batch->deleted, 'Deleted @number @type node.', 'Deleted @number @type nodes.', array('@number' => $batch->deleted, '@type' => node_get_types('name', $this->config['content_type'])))); + } else { - drupal_set_message(t('There is no new content.')); + drupal_set_message(t('There have been no content changes.')); } $batch->setProgress(FEEDS_PROCESSING, FEEDS_BATCH_COMPLETE); } @@ -138,6 +162,28 @@ class FeedsNodeProcessor extends FeedsPr } /** + * Keep nodes in sync with feed items. + */ + public function syncNodes($batch, $source, $batch_started) { + $sql = "SELECT n.nid FROM {feeds_node_item} fni LEFT JOIN {node} n ON fni.nid = n.nid WHERE fni.feed_nid = %d AND n.status = 1 AND fni.touched < %d"; + $result = db_query($sql, $source->feed_nid, $batch_started); + + while($node = db_fetch_object($result)) { + if ($this->config['sync'] == FEEDS_SYNC_REMOVED_UNPUBLISH) { + $node = node_load(array('nid' => $node->nid)); + $node->status = 0; + $node->log = 'Unpublished by FeedsNodeProcessor - '. format_date(time()); + node_save($node); + $batch->unpublished++; + } + elseif ($this->config['sync'] == FEEDS_SYNC_REMOVED_DELETE) { + node_delete($node->nid); + $batch->deleted++; + } + } + } + + /** * Override parent::configDefaults(). */ public function configDefaults() { @@ -150,6 +196,7 @@ class FeedsNodeProcessor extends FeedsPr 'expire' => FEEDS_EXPIRE_NEVER, 'mappings' => array(), 'author' => 0, + 'sync' => FEEDS_SYNC_REMOVED_IGNORE, ); } @@ -205,6 +252,17 @@ class FeedsNodeProcessor extends FeedsPr ), '#default_value' => $this->config['update_existing'], ); + $form['sync'] = array( + '#type' => 'radios', + '#title' => t('Keep nodes and feed items in sync'), + '#description' => t('Select how nodes corresponding to removed feed items should be handled. This applies if you want to only display nodes that map to items currently in the feed.'), + '#options' => array( + FEEDS_SYNC_REMOVED_IGNORE => 'Do nothing', + FEEDS_SYNC_REMOVED_UNPUBLISH => 'Unpublish the node', + FEEDS_SYNC_REMOVED_DELETE => 'Delete the node', + ), + '#default_value' => $this->config['sync'], + ); return $form; } @@ -351,6 +409,7 @@ class FeedsNodeProcessor extends FeedsPr if ($populate) { $node->type = $this->config['content_type']; $node->changed = FEEDS_REQUEST_TIME; + $node->status = 1; $node->format = $this->config['input_format']; $node->feeds_node_item = new stdClass(); $node->feeds_node_item->id = $this->id; @@ -358,6 +417,7 @@ class FeedsNodeProcessor extends FeedsPr $node->feeds_node_item->feed_nid = $feed_nid; $node->feeds_node_item->url = ''; $node->feeds_node_item->guid = ''; + $node->feeds_node_item->touched = FEEDS_REQUEST_TIME; } static $included;