I'm using FeedAPI Node and the Common Syndication Parser for a site my organization is working on. I'm pulling in feeds from several outside blogs, and creating nodes for each item so that we can pick and choose which ones get showcased on this site -- and also be able to rewrite the teaser, if necessary, to give the proper context.

The goal, though, is to send a reader directly to the original post -- not to the node that's been created on my site. That was easy to do in the custom node.tpl.php file for this content type, by simply changing...

 <h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>

to this:

 <h2><a href="<?php print $node->links['feedapi_original']['href'] ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>

In my site's own RSS feed, though, headlines for any of these items that "make the cut" and get published link back to the node on mysite, not -- ie, back to $node_url rather than to $node->links['feedapi_original']['href'].

I floated this question over in the RSS & Aggregation Group a couple days ago, and have done some more digging and experimenting since then. This post at 2Bits seems to come closest to offering a way to alter the RSS structure without hacking core, by invoking hook_nodeapi, but my attempt to modify their "add-an-image" example to instead change the URL:

<?php
function custom_nodeapi(&$node, $op, $teaser, $page) {
switch($op) {
case 'rss item':
$extra = NULL;
if ($node->type == 'affiliated_blog') {
// This is an node created by Feed-API slurping, use a different URL
$link = $node->links['feedapi_original']['href'];
return;
}
}
}

... isn't cutting it.

Is there an easy way to do this? Something obvious that I'm missing? Any help would be much appreciated.

(And sorry for the long-winded post. But if I am on the right track, hopefully the links and code snippets will be helpful to others down the line.)

TKS

Comments

t3knoid’s picture

Your requirement sounds similar to what I want to do. I have a Digg-like website that uses the Storylink module. What I wanted was to have the RSS feeds point to the original link instead of the nodes in my website. I was able to accomplish thi by doing the following:

In the function node_feed inside node.module, I replaced the following line:

$link = url("node/$node->nid", NULL, NULL, 1);

with the following:

$links = links_load_links_for_node($node->nid, 'vote_storylink', 0, TRUE);
$link = $links[0]['url'];

TKS’s picture

Thanks for the tip. I want to avoid hacking core if at all possible, but this gives me another example to play with. Will report back on any progress...

TKS

budda’s picture

Can you not use Views with RSS argument selector. Then theme the View output if needed?

TKS’s picture

Status: Active » Fixed

A very belated update on this -- just realized and never posted back on how we solved this problem.

The short answer: Hire someone who knows more than you do.... :) Dopry wrote a quickie module for us that overrides node/feed and passes it through the theme layer.

The module itself looks like this:


/**
 * This module overrides node/feed and passes it through the theme layer
 * Allowing developers to modify the feed structure.
 */

function themefeed_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array('path' => 'rss.xml', 'title' => t('RSS feed'),
      'callback' => 'themefeed_feed',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
  }
  return $items;
}

function themefeed_feed($nodes = 0, $channel = array()) {
  global $base_url, $locale;

  if (!$nodes) {
    $nodes = db_query_range(db_rewrite_sql('SELECT n.nid, n.created FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.created DESC'), 0, variable_get('feed_default_items', 10));
  }

  $item_length = variable_get('feed_item_length', 'teaser');
  $namespaces = array('xmlns:dc="http://purl.org/dc/elements/1.1/"');

  while ($node = db_fetch_object($nodes)) {
    // Load the specified node:
    $item = node_load($node->nid);
    $item->rss_link = url("node/$node->nid", NULL, NULL, 1);

    if ($item_length != 'title') {
      $teaser = ($item_length == 'teaser') ? TRUE : FALSE;

      // Filter and prepare node teaser
      if (node_hook($item, 'view')) {
        $item = node_invoke($item, 'view', $teaser, FALSE);
      }
      else {
        $item = node_prepare($item, $teaser);
      }

      // Allow modules to change $node->teaser before viewing.
      node_invoke_nodeapi($item, 'view', $teaser, FALSE);
    }

    // Allow modules to add additional item fields and/or modify $item
    $extra = node_invoke_nodeapi($item, 'rss item');
    $extra = array_merge($extra, array(array('key' => 'pubDate', 'value' =>  date('r', $item->created)), array('key' => 'dc:creator', 'value' => $item->name), array('key' => 'guid', 'value' => $item->nid .' at '. $base_url, 'attributes' => array('isPermaLink' => 'false'))));
    foreach ($extra as $element) {
      if ($element['namespace']) {
        $namespaces = array_merge($namespaces, $element['namespace']);
      }
    }

    // Prepare the item description
    switch ($item_length) {
      case 'fulltext':
        $item_text = $item->body;
        break;
      case 'teaser':
        $item_text = $item->teaser;
        if ($item->readmore) {
          $item_text .= '<p>'. l(t('read more'), 'node/'. $item->nid, NULL, NULL, NULL, TRUE) .'</p>';
        }
        break;
      case 'title':
        $item_text = '';
        break;
    }

    $items .= format_rss_item($item->title, $item->rss_link, $item_text, $extra);
  }

  $channel_defaults = array(
    'version'     => '2.0',
    'title'       => variable_get('site_name', 'Drupal') .' - '. variable_get('site_slogan', ''),
    'link'        => $base_url,
    'description' => variable_get('site_mission', ''),
    'language'    => $locale
  );
  $channel = array_merge($channel_defaults, $channel);

  $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
  $output .= "<rss version=\"". $channel["version"] ."\" xml:base=\"". $base_url ."\" ". implode(' ', $namespaces) .">\n";
  $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
  $output .= "</rss>\n";

  drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
  print $output;
}

function themefeed_nodeapi(&$node, $op) {
  if ($op == 'rss item' && $node->type == 'affiliated_blog') {
    // Do the link generation from node_view. I really wish feedapi just added the
    // link to the original article to the nodes in load... would make life easier.
    $item = $node;
    $item->links = module_invoke_all('link', 'node', $node);
    foreach (module_implements('link_alter') AS $module) {
      $function = $module .'_link_alter';
      $function($item, $item->links);
    }
    //    drupal_set_message('affiliate :'. print_r($node,1));
    // This is an node created by Feed-API slurping, use a different URL
    $node->rss_link = $item->links['feedapi_original']['href'];
  }
}

There's also a .install file, to make sure the module runs in the proper sequence:

// make sure themefeed runs after node so it overwrites the node.modules
// menu callbacks.
function themefeed_install() {
  $node_weight = db_result(db_query("SELECT weight FROM {system} WHERE name='node'"));
  db_query("UPDATE system SET weight=%d WHERE name='themefeed'", $node_weight + 1);
}

And then in template.php, we now have the following override:

// ==================== Override of ThemeFeed Module's theme_rss_item ====================


/**
 * Format a single RSS item.
 *
 * Arbitrary elements may be added using the $args associative array.
 */
function phptemplate_rss_item($title, $link, $description, $args = array()) {
  $output = "<item>\n";
  if ($node->type == 'blogpost') {
  $output .= ' <title>[BLOGPOST]'. check_plain($title) ."</title>\n";
	}
  else {
  $output .= ' <title>[OTHER]'. check_plain($title) ."</title>\n";
  }
  //  $output .= ' <title>'. check_plain($title) ."</title>\n";
  $output .= ' <link>'. check_url($link) ."</link>\n";
  $output .= ' <description>'. check_plain($description) ."</description>\n";
  $output .= format_xml_elements($args);
  $output .= "</item>\n";

  return $output;
}

Hope that helps...

Anonymous’s picture

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.