Index: views/itunes.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/itunes/views/itunes.views.inc,v
retrieving revision 1.1
diff -u -p -r1.1 itunes.views.inc
--- views/itunes.views.inc	20 May 2010 22:55:48 -0000	1.1
+++ views/itunes.views.inc	28 Nov 2010 23:10:21 -0000
@@ -25,5 +25,90 @@ function itunes_views_plugins() {
         'type' => 'feed',
       ),
     ),
+    'row' => array(
+      'itunes_rss' => array(
+        'title' => t('iTunes RSS Item'),
+        'help' => t("Customize the sources of data for the episodes. Use this if the Node style doesn't meet your needs."),
+        'handler' => 'itunes_plugin_row_rss',
+        'path' => drupal_get_path('module', 'itunes') . '/views',
+        'uses fields' => TRUE,
+        'type' => 'feed',
+        'parent' => 'node_rss',
+        'uses options' => TRUE,
+      ),
+    ),
   );
+}
+
+
+/**
+ * Implementation of hook_views_data()
+ */
+function itunes_views_data() {
+  // Basic table information.
+  $data['itunes_item']['table']['group']  = t('iTunes');
+
+  // Join to 'node' as a base table.
+  $data['itunes_item']['table']['join'] = array(
+    'node' => array(
+      'left_field' => 'vid',
+      'field' => 'vid',
+    ),
+  );
+
+  // ----------------------------------------------------------------
+  // Fields
+  $data['itunes_item']['summary'] = array(
+    'title' => t('Summary'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  $data['itunes_item']['subtitle'] = array(
+    'title' => t('Subtitle'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  $data['itunes_item']['explicit'] = array(
+    'title' => t('Explicit'),
+
+  );
+  $data['itunes_item']['block'] = array(
+    'title' => t('Blocked'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+      'label' => t('Blocked'),
+      'type' => 'yes-no',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  return $data;
 }
\ No newline at end of file
Index: views/itunes_plugin_row_rss.inc
===================================================================
RCS file: views/itunes_plugin_row_rss.inc
diff -N views/itunes_plugin_row_rss.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ views/itunes_plugin_row_rss.inc	28 Nov 2010 23:10:21 -0000
@@ -0,0 +1,366 @@
+<?php
+// $Id: itunes_plugin_style_rss.inc,v 1.1 2010/05/20 22:55:48 drewish Exp $
+
+/**
+ * @file
+ * Contains the RSS row plugin.
+ */
+
+/**
+ * iTunes specific row plugin to render the feed element.
+ *
+ * @ingroup views_style_plugins
+ */
+class itunes_plugin_row_rss extends views_plugin_row_node_rss {
+  var $elements;
+
+  function itunes_plugin_row_rss() {
+    $this->elements = array(
+      'author' => array('title' => t('Author')),
+      'subtitle' => array('title' => t('Subtitle')),
+      'summary' => array('title' => t('Summary')),
+      'enclosure_url' => array('title' => t('Enclosure URL')),
+      'enclosure_mime' => array('title' => t('Enclosure MIME Type')),
+      'enclosure_size' => array('title' => t('Enclosure Size')),
+      'duration' => array('title' => t('Duration')),
+      'block' => array('title' => t('Blocked')),
+      'explicit' => array('title' => t('Explicit')),
+      'keywords' => array('title' => t('Keywords')),
+    );
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['sources'] = array('translatable' => FALSE, 'default' => array());
+    foreach ($this->elements as $key => $info) {
+      $options['sources']['default'][$key] = NULL;
+    }
+
+    return $options;
+  }
+
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $handlers = $this->display->handler->get_handlers('field');
+    if (empty($handlers)) {
+      $form['error_markup'] = array(
+        '#value' => t('You need at least one field before you can configure your feed item settings'),
+        '#prefix' => '<div class="error form-item description">',
+        '#suffix' => '</div>',
+      );
+      return;
+    }
+
+
+    // Create an array of allowed columns from the data we know:
+    $field_names = array(NULL => t('- Not selected -')) + $this->display->handler->get_field_labels();
+
+    $form['sources'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Sources'),
+    );
+    foreach ($this->elements as $key => $info) {
+      $form['sources'][$key] = array(
+        '#type' => 'select',
+        '#title' => $info['title'],
+        '#description' => isset($info['description']) ? $info['description'] : NULL,
+        '#options' => $field_names,
+        '#default_value' => $this->options['sources'][$key],
+      );
+    }
+  }
+
+  function render($row) {
+    $values = array();
+
+    // The values are stored in $row by the field alias so we need to look up
+    // the alias before we can fetch the value.
+    foreach ($this->elements as $key => $info) {
+      if (!empty($this->options['sources'][$key])) {
+        $field_name = $this->options['sources'][$key];
+        $field_alias = $this->view->field[$field_name]->field_alias;
+        $values[$key] = $row->{$field_alias};
+      }
+    }
+
+    return '';
+
+      // For the most part, this code is taken from node_feed() in node.module
+    global $base_url;
+
+    $nid = $row->{$this->field_alias};
+    if (!is_numeric($nid)) {
+      return;
+    }
+
+    $item_length = $this->options['item_length'];
+    if ($item_length == 'default') {
+      $item_length = variable_get('feed_item_length', 'teaser');
+    }
+
+    // Load the specified node:
+    $node = node_load($nid);
+    if (empty($node)) {
+      return;
+    }
+
+    $node->build_mode = NODE_BUILD_RSS;
+
+    if ($item_length != 'title') {
+      $teaser = ($item_length == 'teaser') ? TRUE : FALSE;
+
+      // Filter and prepare node teaser
+      if (node_hook($node, 'view')) {
+        $node = node_invoke($node, 'view', $teaser, FALSE);
+      }
+      else {
+        $node = node_prepare($node, $teaser);
+      }
+
+      // Allow modules to change $node->teaser before viewing.
+      node_invoke_nodeapi($node, 'view', $teaser, FALSE);
+    }
+
+    // Set the proper node part, then unset unused $node part so that a bad
+    // theme can not open a security hole.
+    $content = drupal_render($node->content);
+    if ($teaser) {
+      $node->teaser = $content;
+      unset($node->body);
+    }
+    else {
+      $node->body = $content;
+      unset($node->teaser);
+    }
+
+    // Allow modules to modify the fully-built node.
+    node_invoke_nodeapi($node, 'alter', $teaser, FALSE);
+
+    $item = new stdClass();
+    $item->title = $node->title;
+    $item->link = url("node/$row->nid", array('absolute' => TRUE));
+    $item->nid = $node->nid;
+    $item->readmore = $node->readmore;
+
+    // Allow modules to add additional item fields and/or modify $item
+    $extra = node_invoke_nodeapi($node, 'rss item');
+    $item->elements = array_merge($extra,
+      array(
+        array('key' => 'pubDate', 'value' => gmdate('r', $node->created)),
+        array(
+          'key' => 'dc:creator',
+          'value' => $node->name,
+          'namespace' => array('xmlns:dc' => 'http://purl.org/dc/elements/1.1/'),
+        ),
+        array(
+          'key' => 'guid',
+          'value' => $node->nid . ' at ' . $base_url,
+          'attributes' => array('isPermaLink' => 'false')
+        ),
+      )
+    );
+
+    // Custom elements
+    $item->elements[] = array(
+      'key' => 'enclosure',
+      'value' => '',
+      'attributes' => array(
+        'url' => $values['enclosure_url'],
+        'length' => !empty($values['enclosure_size']) ? (int) $values['enclosure_size'] : 0,
+        'type' => !empty($values['enclosure_mime']) ? check_plain($values['enclosure_mime']) : 'audio/mpeg',
+      ),
+    );
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:duration',
+      'value' => (int) $values['duration'],
+    );
+
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:keywords',
+      'value' => implode(',', array_slice($keywords, 0, 12)),
+    );
+
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:author',
+      'value' => $values['author'],
+    );
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:summary',
+      'value' => $values['summary'],
+    );
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:subtitle',
+      'value' => $values['subtitle'],
+    );
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:block',
+      'value' => 'yes',
+    );
+    $item->elements[] = array(
+      'namespace' => array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'),
+      'key' => 'itunes:explicit',
+      'value' => itunes_explicit($values['explicit']),
+    );
+
+
+    foreach ($item->elements as $element) {
+      if (isset($element['namespace'])) {
+        $this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $element['namespace']);
+      }
+    }
+
+    // Prepare the item description
+    switch ($item_length) {
+      case 'fulltext':
+        $item->description = $node->body;
+        break;
+      case 'teaser':
+        $item->description = $node->teaser;
+        if (!empty($item->readmore)) {
+          $item->description .= '<p>' . l(t('read more'), 'node/' . $item->nid, array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . '</p>';
+        }
+        break;
+      case 'title':
+        $item->description = '';
+        break;
+    }
+
+    return theme($this->theme_functions(), $this->view, $this->options, $item);
+  }
+
+
+
+  function _render($row) {
+    // For the most part, this code is taken from node_feed() in node.module
+    global $base_url;
+
+    $item_length = $this->options['item_length'];
+    if ($item_length == 'default') {
+      $item_length = variable_get('feed_item_length', 'teaser');
+    }
+
+    if (empty($this->view->style_plugin->namespaces)) {
+      $this->view->style_plugin->namespaces['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
+    }
+
+    // Load the specified node:
+    $item = node_load($row->nid);
+    $item->build_mode = NODE_BUILD_RSS;
+    $item->link = url("node/$row->nid", array('absolute' => TRUE));
+
+    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' => gmdate('r', $item->created)),
+        // The author should be an email address. Need to add this in.
+        //array('key' => 'author', 'value' => $item->name),
+      )
+    );
+    foreach ($extra as $element) {
+      if (isset($element['namespace'])) {
+        $this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $element['namespace']);
+      }
+    }
+
+    // Prepare the item description
+    switch ($item_length) {
+      case 'fulltext':
+        $item_text = $item->body;
+        break;
+      case 'teaser':
+        $item_text = $item->teaser;
+        if (!empty($item->readmore)) {
+          $item_text .= '<p>' . l(t('read more'), 'node/' . $item->nid, array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . '</p>';
+        }
+        break;
+      case 'title':
+        $item_text = '';
+        break;
+    }
+
+    $stripped_item_text = strip_tags($item_text);
+
+    if (strlen($stripped_item_text) > 255) {
+      $item_subtitle = substr($stripped_item_text, 0, 252) .'...';
+    }
+    else {
+      $item_subtitle = $stripped_item_text;
+    }
+
+    if (!getid3_load(TRUE)) {
+      return NULL;
+    }
+    $getid3 = new getID3;
+    foreach ( $this->view->field as $id => $field ) {
+      if ($field->content_field['widget']['type'] == 'filefield_widget') {
+        foreach ( $item->{$field->content_field['field_name']} as $file ) {
+          $info = $getid3->analyze($file['filepath']);
+          $file_extra = array();
+          $file_extra[] = array(
+            'key' => 'enclosure',
+            'attributes'  =>  array(
+              'url' => file_create_url($file['filepath']),
+              'length' => $file['filesize'],
+              'type' => $file['filemime'],
+            ),
+          );
+          $file_extra[] = array(
+            'key' => 'itunes:duration',
+            'value' => $info['playtime_string'],
+          );
+          $file_extra[] = array(
+            'key' => 'itunes:author',
+            'value' => $info['tags']['id3v2']['artist'][0],
+          );
+
+          $file_extra[] = array(
+            'key' => 'itunes:subtitle',
+            'value' => str_replace('&amp;', '&', $item_subtitle),
+          );
+          $file_extra[] = array(
+            'key' => 'itunes:summary',
+            'value' => str_replace('&amp;', '&', $stripped_item_text),
+          );
+          $file_extra[] = array(
+            'key' => 'guid',
+            'value' => file_create_url($file['filepath']),
+            'attributes' => array('isPermaLink' => 'false'),
+          );
+          $file_extra = array_merge($extra, $file_extra);
+          /*
+           * The following function takes title, link, description and then
+           * all additional XML elements.  For the title we'll use the node
+           * title.  Link serves no real purpose in a podcast.  Description
+           * is overridden by the extra "subtitle" tag but we'll keep it for
+           * completeness with RSS and use the node teaser.
+          */
+          $output .= format_rss_item($item->title, $item->link, $item_text, $file_extra );
+        }
+      }
+    }
+    return $output;
+  }
+}
