nid);
while ($f = db_fetch_object($result)) {
$node->feedapi_node = $f;
$feed_nids[$f->feed_nid] = $f->feed_nid;
}
if ($node->feedapi_node) {
$node->feedapi_node->feed_nids = $feed_nids;
unset($node->feedapi_node->feed_nid);
}
break;
case 'insert':
if ($node->feedapi_node->feed_item) {
// Why do we stick the nid on the feed item here?
$node->feedapi_node->feed_item->nid = $node->nid;
foreach ($node->feedapi_node->feed_nids as $feed_nid) {
db_query("INSERT INTO {feedapi_node_item_feed} (feed_nid, feed_item_nid) VALUES (%d, %d)", $feed_nid, $node->nid);
}
$feed_item = $node->feedapi_node->feed_item;
$arrived = time();
db_query("INSERT INTO {feedapi_node_item} (nid, url, timestamp, arrived, guid) VALUES (%d, '%s', %d, %d, '%s')", $node->nid, $feed_item->options->original_url, $feed_item->options->timestamp, $arrived, $feed_item->options->guid);
// Construct $node->feedapi_node component.
// This should look the same as when loaded from DB.
$node->feedapi_node->url = $feed_item->options->original_url;
$node->feedapi_node->guid = $feed_item->options->guid;
$node->feedapi_node->arrived = $arrived;
$node->feedapi_node->timestamp = $feed_item->options->timestamp;
$node->feedapi_node->nid = $node->nid;
}
break;
case 'update':
if ($node->feedapi_node) {
if ($node->feedapi_node->feed_item) {
$feed_item = $node->feedapi_node->feed_item;
db_query("UPDATE {feedapi_node_item} SET url = '%s', timestamp = %d, guid = '%s' WHERE nid = %d", $feed_item->options->original_url, $feed_item->options->timestamp, $feed_item->options->guid, $node->nid);
}
db_query('DELETE FROM {feedapi_node_item_feed} WHERE feed_item_nid = %d', $node->nid);
foreach ($node->feedapi_node->feed_nids as $feed_nid) {
db_query("INSERT INTO {feedapi_node_item_feed} (feed_nid, feed_item_nid) VALUES (%d, %d)", $feed_nid, $node->nid);
}
}
break;
case 'delete':
if ($node->feedapi_node) {
db_query('DELETE FROM {feedapi_node_item} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {feedapi_node_item_feed} WHERE feed_item_nid = %d', $node->nid);
}
break;
}
}
/**
* Implementation of hook_link().
*/
function feedapi_node_link($type, $node = NULL) {
if ($type == 'node') {
if ($node->feedapi_node) {
$result = db_query("SELECT n.title, n.nid FROM {node} n WHERE n.nid IN (%s) ORDER BY title DESC", implode(', ', $node->feedapi_node->feed_nids));
$owner_feeds_num = db_num_rows($result);
while ($feed = db_fetch_object($result)) {
$links['feedapi_feed'. ($owner_feeds_num == 1 ? '' : '_'. $feed->nid)] = array(
'title' => t('Feed:') .' '. $feed->title,
'href' => 'node/'. $feed->nid,
);
}
if ($node->feedapi_node->url) {
$links['feedapi_original'] = array(
'title' => t('Original article'),
'href' => $node->feedapi_node->url,
);
}
return $links;
}
}
}
/**
* Implementation of hook_feedapi_settings_form().
* If a module provides parsers and processors it MUST evaluate the $type variable
* to return different forms for parsers and processors.
* There might be a better term for parsers and processors than $type.
*/
function feedapi_node_feedapi_settings_form($type) {
switch ($type) {
case 'processors':
$ct_types = node_get_types();
$ct_options = array();
if (is_array($ct_types)) {
foreach ($ct_types as $key => $data) {
$ct_options[$key] = $data->name;
}
}
$form['content_type'] = array(
'#type' => 'select',
'#title' => t('Node type of feed items'),
'#default_value' => 'story',
'#options' => $ct_options,
'#description' => t('Choose the node type for feed item nodes created by this feed.'),
);
$form['node_date'] = array(
'#type' => 'radios',
'#title' => t('Created date of item nodes'),
'#options' => array('feed' => t('Retrieve from feed'), 'current' => t('Use time of download')),
'#default_value' => 'feed',
);
$form['promote'] = array(
'#type' => 'textfield',
'#title' => t('Promoted items'),
'#description' => t('The newest N items per feed will be promoted to front page. Leave empty and FeedAPI does not alter the promote property of the feed items.'),
'#default_value' => 3,
);
$form['x_dedupe'] = array(
'#type' => 'radios',
'#title' => t('Duplicates'),
'#description' => t('If you choose "check for duplicates on all feeds", a feed item will not be created if it already exists on *ANY* feed. Instead, the existing feed item will be linked to the feed. If you are not sure, choose the first option.'),
'#options' => array(0 => t('Check for duplicates only within feed'), 1 => t('Check for duplicates on all feeds')),
'#default_value' => 0,
);
break;
}
return $form;
}
/**
* Implementation of hook_feedapi_item().
*/
function feedapi_node_feedapi_item($op) {
switch ($op) {
case 'type':
return array("XML feed");
default:
if (function_exists('_feedapi_node_'. $op)) {
$args = array_slice(func_get_args(), 1);
return call_user_func_array('_feedapi_node_'. $op, $args);
}
}
}
/**
* Implements hook_feedapi_after_refresh($feed).
* Handle the promote N items to the frontpage setting
*/
function feedapi_node_feedapi_after_refresh($feed) {
if ($promote = $feed->settings['processors']['feedapi_node']['promote']) {
$result = db_query("SELECT n.nid FROM {node} n JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = n.nid WHERE ff.feed_nid = %d AND n.promote = 1", $feed->nid);
$nids_promoted_now = array();
$nids_top_n = array();
while ($item = db_fetch_array($result)) {
$nids_promoted_now[] = $item['nid'];
}
$result = pager_query("SELECT n.nid FROM {node} n JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = n.nid WHERE ff.feed_nid = %d ORDER BY n.created DESC", $promote, 0, NULL, $feed->nid);
while ($item = db_fetch_array($result)) {
$nids_top_n[] = $item['nid'];
}
$to_promote = array_diff($nids_top_n, $nids_promoted_now);
$to_demote = array_diff($nids_promoted_now, $nids_top_n);
if (count($to_promote) > 0) {
db_query("UPDATE {node} SET promote = 1 WHERE nid IN (%s)", implode(',', $to_promote));
}
if (count($to_demote) > 0) {
db_query("UPDATE {node} SET promote = 0 WHERE nid IN (%s)", implode(',', $to_demote));
}
}
}
/**
* Check for expired items, pass them to the item_expire function
*
* @TO DO Add cron timeout checking here, there may be too many items (nodes) to delete
*
* We implement the same logic as a db query. The old code is
*
* if (isset($item->arrived) || isset($item->timestamp)) {
* $diff = abs(time() - (isset($item->timestamp) ? $item->timestamp : $item->timestamp));
* if ($diff > $settings['items_delete']) {
* ................
* }
* }
*/
function _feedapi_node_expire($feed, $settings) {
$count = 0;
if ($settings['items_delete'] > FEEDAPI_NEVER_DELETE_OLD) {
$timexpire = time() - $settings['items_delete'];
// @ TODO Review this query conditions
$result = db_query("SELECT * FROM {feedapi_node_item} fn JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fn.nid WHERE ff.feed_nid = %d AND ( (fn.timestamp > 0 AND fn.timestamp < %d) OR (fn.timestamp = 0 AND fn.arrived > 0 AND fn.arrived < %d) )", $feed->nid, $timexpire, $timexpire);
while ($item = db_fetch_object($result)) {
// We callback feedapi for deleting
feedapi_expire_item($feed, $item);
$count++;
}
}
return $count;
}
/**
* Create a node from the feed item
* Store the relationship between the node and the feed item
*/
function _feedapi_node_save($feed_item, $feed_nid, $settings = array()) {
// Don't save anything if neither url nor guid given.
if (!$feed_item->options->original_url) {
if (!$feed_item->options->guid) {
return $feed_item;
}
}
// Construct the node object
$node = new stdClass();
if (isset($feed_item->nid)) {
$node->nid = $feed_item->nid;
}
$node->type = !empty($settings['content_type']) ? $settings['content_type'] : variable_get('feedapi_node_type', 'story');
// Get the default options from the cont
$options = variable_get('node_options_'. $node->type, FALSE);
if (is_array($options)) {
$node->status = in_array('status', $options) ? 1 : 0;
$node->promote = in_array('promote', $options) ? 1 : 0;
$node->sticky = in_array('sticky', $options) ? 1 : 0;
}
else {
$node->status = 1;
}
$feed_node = node_load($feed_nid);
$node->title = $feed_item->title;
if (empty($node->title) && !empty($feed_item->description)) {
// Explode to words and use the first 3 words.
$words = preg_split("/[\s,]+/", $feed_item->description);
$node->title = $words[0] .' '. $words[1] .' '. $words[2];
}
$node->uid = $feed_node->uid;
$node->created = (isset($settings['node_date']) && $settings['node_date'] == 'feed') ? $feed_item->options->timestamp : time();
$node->body = $feed_item->description;
$node->teaser = node_teaser($feed_item->description);
//********************************
// hack by Robert Buzink, broot.nl.
// simplify feed_item object
$feed_item_simple = _feedapi_mapper_obj2array($feed_item);
$feed_item_simple = _feedapi_mapper_simplify_raw($feed_item_simple, $feed_node->feed->parsers);
// loops through the tax terms of the feed-node and looks for specific vocabularies.
foreach ($feed_node->taxonomy as $taxterm) {
// if a custom body tag is given, do magic
if ($taxterm->vid == 4) {
//empty the body
$node->body = "";
$node->teaser ="";
$bodytag = $taxterm->name;
// extract the array/object path from the taxonomy variable as an array
$bodytag_steps = split("--",$bodytag);
$n = 0;
// get the value of the custom rss field
foreach ($bodytag_steps as $step) {
if ($n == 0) {
$custombody = $feed_item_simple[$step];
} else {
if (is_array($custombody)) {
$custombody = $custombody[$step];
} else {
$custombody = $custombody->$step;
}
}
$n++;
}
// if the rss-field is an array or object, print it to the body in a human readable form.
if (is_array($custombody) || is_object($custombody)) {
$custombody = print_r($custombody, true);
}
//assign the custombody to the node-body
$node->body = $custombody;
$node->teaser = node_teaser($custombody);
//drupal_set_message("Custombody: ".$custombody,"error"); //DEBUG
}
// if a custom title tag is given, do magic
if ($taxterm->vid == 5) {
//empty the title
$node->title = "";
$titletag = $taxterm->name;
// extract the array/object path from the taxonomy variable as an array
$titletag_steps = split("--",$titletag);
$n = 0;
// get the value of the custom rss field
foreach ($titletag_steps as $step) {
if ($n == 0) {
$customtitle = $feed_item_simple[$step];
} else {
if (is_array($customtitle)) {
$customtitle = $customtitle[$step];
} else {
$customtitle = $customtitle->$step;
}
}
$n++;
}
// if the rss-field is an array or object, print it to the custom title in a human readable form.
if (is_array($customtitle) || is_object($customtitle)) {
$customtitle = print_r($customtitle, true);
}
//assign the customtitle to the node-title
if ($customtitle) {
$node->title = $customtitle;
}
//////// DEBUG
//drupal_set_message("Customtitle: ".$customtitle,"error"); //DEBUG
//drupal_set_message("Feed-item object:\n
".print_r($feed_item_simple, true)."","error"); //DEBUG //////// } } //******************************** // Stick feed item on node so that add on modules can act on it. // A feed item can come in from more than one feed. $node->feedapi_node->feed_nids[$feed_nid] = $feed_nid; $node->feedapi_node->feed_item = $feed_item; // For backwards compatibility - todo: move to using feedapi_node->feed_nids and feedapi_node->feed_item. $node->feedapi->feed_nid = $feed_nid; $node->feedapi->feed_item = $feed_item; node_object_prepare($node); // If there are dupes on other feeds, don't create new feed item, but link this feed // to existing feed item. // Heads up: if there is a duplicate on the SAME feed, // _feedapi_node_save() won't even be called. if (isset($feed_item->feedapi_node->duplicates)) { foreach ($feed_item->feedapi_node->duplicates as $fi_nid => $f_nids) { $feed_item_node = node_load($fi_nid); $feed_item_node->feedapi_node->feed_nids[$feed_nid] = $feed_nid; node_object_prepare($feed_item_node); node_save($feed_item_node); } } else { node_save($node); } return $feed_item; } /** * Update a node which already assigned to a feed item */ function _feedapi_node_update($feed_item, $feed_nid, $settings = array()) { // Determine which node is assigned to this item if ($feed_item->options->guid) { $node = db_fetch_object(db_query("SELECT nid FROM {feedapi_node_item} WHERE guid = '%s'", $feed_item->options->guid)); } else { $node = db_fetch_object(db_query("SELECT nid FROM {feedapi_node_item} WHERE url = '%s'", $feed_item->options->original_url)); } $feed_item->nid = $node->nid; _feedapi_node_save($feed_item, $feed_nid, $settings); return $feed_item; } /** * Delete a node which already assigned to a feed item */ function _feedapi_node_delete($feed_item) { if (isset($feed_item->nid)) { node_delete($feed_item->nid); } else { // Let's throw an error on the off chance we land here. watchdog('feedapi_node', t('No nid on feed item to delete.')); } } /** * Add to the feed item object some data to identify the node assigned */ function _feedapi_node_load($feed_item, $feed_nid) { $item = db_fetch_object(db_query("SELECT * FROM {feedapi_node_item} WHERE nid = %d", $feed_item->nid)); $feed_item->nid = $item->nid; $feed_item->arrived = $item->arrived; $feed_item->options->original_url = $item->url; $feed_item->options->guid = $item->guid; $feed_item->options->timestamp = $item->timestamp; $content = node_load($item->nid); $feed_item->description = $content->body; $feed_item->title = $content->title; $feed_item->teaser = $content->teaser; return $feed_item; } /** * Construct the basic information (nid, feed_nid) of all feeds into an array. * * @param $feed * Feed object * @return * The array of feed elements with basic information */ function _feedapi_node_fetch($feed) { $result = db_query("SELECT fni.nid, ff.feed_nid, fni.arrived, fni.timestamp FROM {feedapi_node_item} fni JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fni.nid WHERE ff.feed_nid = %d ORDER BY fni.timestamp DESC", $feed->nid); $items = array(); while ($item = db_fetch_object($result)) { $node = node_load($item->nid); $item->title = $node->title; $items[] = $item; } return $items; } /** * Tell if the feed item was seen before or not at the feed * * @param $feed_item * Feed item object * @param $feed_nid * Feed ID * @return * TRUE if the item is new, FALSE if the item is a duplicated one */ function _feedapi_node_unique($feed_item, $feed_nid, $settings) { // Feed item is duplicate, if URL or GUID are duplicate or if they are both missing. if (isset($feed_item->options->original_url)) { $count = db_result(db_query("SELECT fni.nid FROM {feedapi_node_item} fni JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fni.nid WHERE fni.url = '%s' AND ff.feed_nid = %d", $feed_item->options->original_url, $feed_nid)); if ($count) { return FALSE; } } if (isset($feed_item->options->guid)) { $count = db_result(db_query("SELECT fni.nid FROM {feedapi_node_item} fni JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fni.nid WHERE fni.guid = '%s' AND ff.feed_nid = %d", $feed_item->options->guid, $feed_nid)); if ($count) { return FALSE; } } // If cross feed de-dupeing is enabled, check now whether there is a duplicate item on other feeds. // If so, store duplicates in array. // There is *usually* only one. However, there might be more than one. // Todo: don't link to feed items whose feed is not x_dedupe enabled. if ($settings['x_dedupe']) { if (isset($feed_item->options->original_url)) { $result = db_query("SELECT fni.nid, ff.feed_nid FROM {feedapi_node_item} fni JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fni.nid WHERE ff.feed_nid <> %d AND fni.url = '%s'", $feed_nid, $feed_item->options->original_url); $i = 0; while ($existing_feed_item = db_fetch_object($result)) { $feed_item->feedapi_node->duplicates[$existing_feed_item->nid][] = $existing_feed_item->feed_nid; } } if (!isset($feed_item->feedapi_node->duplicates) && isset($feed_item->options->guid)) { $result = db_query("SELECT fni.nid, ff.feed_nid FROM {feedapi_node_item} fni JOIN {feedapi_node_item_feed} ff ON ff.feed_item_nid = fni.nid WHERE ff.feed_nid <> %d AND fni.guid = '%s'", $feed_nid, $feed_item->options->guid); while ($existing_feed_item = db_fetch_object($result)) { $feed_item->feedapi_node->duplicates[$existing_feed_item->nid][] = $existing_feed_item->feed_nid; } } } if (isset($feed_item->options->original_url) || isset($feed_item->options->guid)) { return TRUE; } // Neither GUID, nor URL present: no unique item. return FALSE; }