views_rss.module 6.43 KB
Newer Older
1
<?php
merlinofchaos's avatar
merlinofchaos committed
2
// $Id$
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

function views_rss_help($section = 'admin/help#views_rss') {
  $output = '';
  switch ($section) {
    case 'admin/modules#description':
      return t('RSS plugin for the views feed selector argument.');
  }
}

/**
 * Provide views plugins for the feed types we support.
 */
function views_rss_views_style_plugins() {
  return array(
    'views_rss' => array(
      'name' => t('Views RSS: RSS feed'),
      'theme' => 'views_rss_feed',
20 21
      'needs_table_header' => TRUE,
      'needs_fields' => TRUE,
merlinofchaos's avatar
merlinofchaos committed
22
      'even_empty' => TRUE,
23 24 25 26
    ),
  );
}

27 28 29 30 31 32 33 34 35
/**
 * While we support the global selector, some might want to allow
 * ONLY RSS feeds so we support a stingy selector too
 */
function views_rss_views_arguments() {
  $arguments = array(
    'rss_feed' => array(
      'name' => t('RSS: RSS Feed Selector'),
      'handler' => 'views_handler_arg_rss_feed',
36 37
      'option' => 'string',
      'help' => t('This argument specifies a specific RSS feed selector; it will only select RSS feeds, unlike the built-in selector which can select pluggable feeds. You may enter the title the feed will advertise in the title field here, and the description of the feed in the option field here.'),
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    ),
  );
  return $arguments;
}

/**
 * handler for our own RSS argument; mimics the feed selector
 */
function views_handler_arg_rss_feed($op, &$query, $argtype, $arg = '') {
  switch($op) {
    case 'summary':
    case 'sort':
    case 'link':
    case 'title':
      break;
    case 'filter':
      // This is a clone of the default selector, but it just invokes ours
      // rather than calling all of them.
56
      views_rss_views_feed_argument('argument', $GLOBALS['current_view'], $arg, $argtype);
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  }
}

/**
 * post view for our own op -- mimics the feed selector
 */
function views_rss_views_post_view($view, $items, $output) {
  foreach ($view->argument as $id => $argument) {
    if ($argument['type'] == 'rss_feed') {
      $feed = $id;
      break;
    }
  }

  if ($feed !== NULL) {
    return views_rss_views_feed_argument('post_view', $view, 'rss_feed');
  }
}

/**
 * feed argument hook that will convert us to RSS or display an icon.
 * the 4th argument isn't part of the hook, but we use it to differentiate
 * when called as a hook or when called manually from views_rss_views_post_view
 */
81
function views_rss_views_feed_argument($op, &$view, $arg, $argdata = NULL) {
82 83
  if ($op == 'argument' && $arg == 'feed') {
    $view->page_type = 'views_rss';
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

    if ($argdata['options']) {
      $view->description = $argdata['options'];
    }

    // reset the 'real url' to the URL without the feed argument.
    $view_args = array();
    $max = count($view->args);
    foreach ($view->args as $id => $view_arg) {
      ++$count;
      if ($view_arg == $arg && $view->argument[$id]['id'] == $argdata['id']) {
        if ($count != $max) {
          $view_args[] = $argdata['wildcard'];
        }
      }
      else {
        $view_args[] = $view_arg;
      }
    }
    $view->feed_url = views_get_url($view, $view_args);
104 105
  }
  else if ($op == 'post_view') {
106 107
    $args = views_post_view_make_args($view, $arg, 'feed');
    $url = views_get_url($view, $args);
108
    $title = filter_xss_admin(views_get_title($view, 'page', $args));
109

110 111 112 113
    if ($view->used_filters) {
      $filters = drupal_query_string_encode($view->used_filters);
    }

114 115
    drupal_add_link(array('rel' => 'alternate',
                          'type' => 'application/rss+xml',
116
                          'title' => check_plain($title),
117
                          'href' => url($url, $filters, NULL, TRUE)));
118
    if ($view->build_type != 'block') {
119
      return theme('feed_icon', url($url, $filters));
120
    }
121 122 123
  }
}

124 125 126
/**
 * plugin that actually displays an RSS feed
 */
127 128 129 130
function theme_views_rss_feed($view, $nodes, $type) {
  if ($type == 'block') {
    return;
  }
131 132 133
  global $base_url;

  $channel = array(
134
    'title'       => filter_xss_admin(views_get_title($view, 'page')),
135
    'link'        => url($view->feed_url ? $view->feed_url : $view->real_url, NULL, NULL, true),
136
    'description' => $view->description,
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  );

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

  // Except for the original being a while and this being a foreach, this is
  // completely cut & pasted from node.module.
  foreach ($nodes as $node) {
    // Load the specified node:
    $item = node_load($node->nid);
    $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')) {
        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);
    }

164 165 166 167 168 169 170 171 172
    // Allow modules to add additional item fields
    $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']);
      }
    }
    
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    // 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, $link, $item_text, $extra);
  }

  $channel_defaults = array(
    'version'     => '2.0',
194
    'language'    => $GLOBALS['locale'],
195 196 197 198 199 200 201 202 203 204
  );
  $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: text/xml; charset=utf-8');
  print $output;
205
  module_invoke_all('exit');
206 207
  exit; 
}