? feedfield-6x-dev.patch Index: aggregation_api.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feedfield/aggregation_api.inc,v retrieving revision 1.2 diff -u -p -r1.2 aggregation_api.inc --- aggregation_api.inc 19 Dec 2006 19:20:09 -0000 1.2 +++ aggregation_api.inc 10 Apr 2009 05:00:24 -0000 @@ -1,11 +1,16 @@ = 0), - vid integer NOT NULL default '0' CHECK (nid >= 0), - field_name varchar(32) NOT NULL default '', - delta integer NOT NULL default '0' CHECK (nid >= 0), - field_title varchar(255) NOT NULL default '', - field_url varchar(255) NOT NULL default '', - field_fid integer NOT NULL default '0' CHECK (nid >= 0), - field_type varchar(32) NOT NULL default '', - PRIMARY KEY (vid,field_name,delta) - )"); - break; - } + drupal_load('module', 'content'); + content_notify('install', 'feedfield'); } -function feedfield_update_1(){ - $items[]=update_sql("ALTER TABLE {node_field_feedfield_data} ADD `field_type` VARCHAR( 32 ) NOT NULL default ''"); - return $items; +/** +* Implementation of hook_uninstall(). +*/ +function feedfield_uninstall() { + drupal_load('module', 'content'); + content_notify('uninstall', 'feedfield'); + } + +/** +* Implementation of hook_enable(). +*/ +function feedfield_enable() { + drupal_load('module', 'content'); + content_notify('enable', 'feedfield'); +} + +/** +* Implementation of hook_disable(). +*/ +function feedfield_disable() { + drupal_load('module', 'content'); + content_notify('disable', 'feedfield'); } -function feedfield_update_2(){ - $items[]=update_sql("ALTER TABLE {node_field_feedfield_data} ADD field_title varchar(255) NOT NULL default ''"); - return $items; -} \ No newline at end of file Index: feedfield.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/feedfield/feedfield.module,v retrieving revision 1.4 diff -u -p -r1.4 feedfield.module --- feedfield.module 17 May 2007 14:27:32 -0000 1.4 +++ feedfield.module 10 Apr 2009 05:00:25 -0000 @@ -1,87 +1,138 @@ array( + 'arguments' => array('element' => NULL), + ), + 'feedfield_formatter_default' => array( + 'arguments' => array('element' => NULL), + ), + ); +} /** * Implementation of hook_field_info(). + * + * Here we indicate that the content module will use its default + * handling for the view of this field. + * + * Callbacks can be omitted if default handing is used. + * They're included here just so this module can be used + * as an example for custom modules that might do things + * differently. */ function feedfield_field_info() { return array( - 'feedfield' => array('label' => 'Feed'), + 'feedfield' => array( + 'label' => t('Feed'), + 'description' => t('Create aggregator feed'), + 'callbacks' => array( + 'tables' => CONTENT_CALLBACK_DEFAULT, + 'arguments' => CONTENT_CALLBACK_DEFAULT, + ), + ), ); } + +/** + * Implementation of hook_field_settings(). + */ +function feedfield_field_settings($op, $field) { + switch ($op) { + case 'database columns': + return array( + /* Only need to store fid, everything else will be stored by Aggregator. + * content_type_* will only serve as a juntion table to link feeds + * to node versions. + */ + 'fid' => array('type' => 'int'), + ); + + } +} + /** * Implementation of hook_field(). + * + * @todo Review this method. I have a feeling there are much better ways to + * accomplish the same functionality. */ function feedfield_field($op, &$node, $field, &$node_field, $teaser, $page) { switch ($op) { case 'load': switch($field['type']) { case 'feedfield': - $result = db_query("SELECT field_url, field_fid, field_type,field_title FROM {node_field_feedfield_data} WHERE vid = %d AND field_name = '%s' ORDER BY delta", $node->vid, $field['field_name']); - while ($value = db_fetch_array($result)) { - // load the feed details from the aggregator in case they have been modified there - $feed=aggregation_get_feed_by_id($value['field_fid']); + + foreach ($node_field as $delta => $item) { + $feed=aggregation_get_feed_by_id($node_field[$delta]['fid']); $values[] = array('value' => array( 'feed' => $feed['url'], - 'fid' => $value['field_fid'], - 'type'=>$value['field_type'], + 'fid' => $feed['fid'], 'title'=>$feed['title'])); } + $additions = array($field['field_name'] => $values); break; } return $additions; - case 'view': - foreach ($node_field as $delta => $item) { - $node_field[$delta]['view'] = _feedfield_field_view($field, $item['value'], $item, $node); - } - return theme('field', $node, $field, $node_field, $teaser, $page); case 'insert': switch($field['type']) { case 'feedfield': foreach ($node_field as $delta => $item) { - if ($item['value']['feed']) { + if ($item['feed']) { // we have a feed url - // You might want to force specific titles, so we pass the title through a theme function - $title=theme('feedfield_title',$node,$field,$item); - - if ($fid=aggregation_sync_feed($title, $item['value']['feed'])) { + if ($node_field[$delta]['fid']= + aggregation_sync_feed($item['title'], $item['feed'])) { // set categories - if (is_array($item['value']['category'])) { - foreach ($item['value']['category'] as $cid => $set) { + if (is_array($item['category'])) { + foreach ($item['category'] as $cid => $set) { // convert categories from option list to value list if ($set) $feed_categories[]=$cid; } } - aggregation_set_feed_categories($fid,$feed_categories); + aggregation_set_feed_categories($node_field[$delta]['fid'], + $feed_categories); - // If we have a new feed ID, then we are going to need to remove the old feed - if (($item['value']['fid']) and ($item['value']['fid'] != $fid)) { - aggregation_remove_feed_items($item['value']['fid']); - aggregation_delete_feed($item['value']['fid']); + /* If we have a new feed ID, then we are going to need to remove + * the old feed + */ + if ($item['fid'] != $node_field[$delta]['fid']) { + aggregation_remove_feed_items($item['fid']); + aggregation_delete_feed($item['fid']); } } // and now insert into our own table - db_query("INSERT INTO {node_field_feedfield_data} (nid, vid, field_name, delta, field_url,field_fid,field_type,field_title) VALUES (%d, %d, '%s', %d, '%s',%d,'%s','%s')", $node->nid, $node->vid, $field['field_name'], $delta, $item['value']['feed'],$fid,$item['value']['type'],$title); - } elseif ($item['value']['fid']) { + db_query("UPDATE content_type_%s SET %s_fid = %d WHERE vid = %d", + $field['type_name'], $field['field_name'], $fid, $node->vid); + } elseif ($item['fid']) { // no feed, but a feed id, that means we have to remove this feed - aggregation_remove_feed_items($item['value']['fid']); - aggregation_delete_feed($item['value']['fid']); + aggregation_remove_feed_items($item['fid']); + aggregation_delete_feed($item['fid']); } } break; @@ -92,7 +143,6 @@ function feedfield_field($op, &$node, $f // Delete and insert, rather than update, in case a field was added. switch($field['type']) { case 'feedfield': - db_query("DELETE FROM {node_field_feedfield_data} WHERE vid = %d AND field_name = '%s'", $node->vid, $field['field_name']); feedfield_field('insert', $node, $field, $node_field, $teaser, $page); break; } @@ -109,342 +159,186 @@ function feedfield_field($op, &$node, $f aggregation_delete_feed($item['value']['fid']); } } - db_query("DELETE FROM {node_field_feedfield_data} WHERE nid = %d AND field_name = '%s'", $node->nid, $field['field_name']); break; } return; } } - +/** + * Implementation of hook_content_is_empty(). + */ +function feedfield_content_is_empty($item, $field) { + if (empty($item['feed'])) { + return TRUE; + } + return FALSE; +} /** * Implementation of hook_widget_info(). +* +* Here we indicate that the content module will handle +* the default value and multiple values for these widgets. +* +* Callbacks can be omitted if default handing is used. +* They're included here just so this module can be used +* as an example for custom modules that might do things +* differently. */ function feedfield_widget_info() { return array( 'feedfield' => array( 'label' => 'Aggregator Feed', 'field types' => array('feedfield'), + 'multiple values' => CONTENT_HANDLE_CORE, + 'callbacks' => array( + 'default value' => CONTENT_CALLBACK_DEFAULT, + ), ), ); } /** * Implementation of hook_widget(). - */ -function feedfield_widget($op, &$node, $field, &$node_field) { - switch ($op) { - case 'form': - $form = array(); - $form[$field['field_name']] = array('#tree' => TRUE,'#type'=>'fieldset','#title'=>$field['widget']['label']); - - if ($field['multiple']) { - // Generate more fields if necessary on preview - if ($_POST['edit'][$field['field_name']]) { - $node_field = $_POST['edit'][$field['field_name']]; +* +* Attach a single form element to the form. It will be built out and +* validated in the callback(s) listed in hook_elements. We build it +* out in the callbacks rather than here in hook_widget so it can be +* plugged into any module that can provide it with valid +* $field information. +* +* Content module will set the weight, field name and delta values +* for each form element. This is a change from earlier CCK versions +* where the widget managed its own multiple values. +* +* If there are multiple values for this field, the content module will +* call this function as many times as needed. +* +* @param $form +* the entire form array, $form['#node'] holds node information +* @param $form_state +* the form_state, $form_state['values'] holds the form values. +* @param $field +* the field array +* @param $delta +* the order of this item in the array of subelements (0, 1, 2, etc) +* +* @return +* the form item for a single element for this field + */ +function feedfield_widget(&$form, &$form_state, $field, $items, $delta = 0) { + $element = array( + '#type' => $field['widget']['type'], + '#default_value' => isset($items[$delta]) ? $items[$delta] : '', + ); + return $element; } - $delta = 0; - // Render feedfield fields for all the entered values - foreach ($node_field as $data) { - if ($data['value']['feed']) { - $form[$field['field_name']][$delta]=array('#tree' => TRUE,'#type'=>'fieldset'); - _feedfield_widget_form($form[$field['field_name']][$delta], $field, $data, $delta); - $delta++; +/** + * Implementation of FAPI hook_elements(). + * + * Any FAPI callbacks needed for individual widgets can be declared here, + * and the element will be passed to those callbacks for processing. + * + * Drupal will automatically theme the element using a theme with + * the same name as the hook_elements key. + * + * Includes a regex to check for valid values as an additional parameter + * the validator can use. The regex can be overridden if necessary. + */ +function feedfield_elements() { + return array( + 'feedfield' => array( + '#input' => TRUE, + '#columns' => array('title', 'feed', 'catagory', 'fid'), + '#process' => array('feedfield_process'), + ), + ); } - } - // Render two additional new feedfield fields - foreach (range($delta, $delta + 1) as $delta) { - $form[$field['field_name']][$delta]=array('#tree' => TRUE,'#type'=>'fieldset'); - _feedfield_widget_form($form[$field['field_name']][$delta], $field, $node_field, $delta); - } - } // end if multiple - else { - // Convert an old 'single' value field into the new array based format - if ($node_field['value']) { - $node_field[0]['value'] = $node_field['value']; - unset($node_field['value']); - } - _feedfield_widget_form($form[$field['field_name']][0], $field, $node_field[0]); - } - return $form; - - case 'validate': - foreach($node_field as $delta => $value) { - if ($value['value']['feed']) { - // Validate the feedfield - if (!feedfield_validate_link($value['value']['feed'])) { - form_set_error($field['field_name'] .']['. $delta. '][value][feed', t('Not a valid URL.')); - } elseif ($existing_feed=aggregation_get_feed_by_url($value['value']['feed'])) { - //url's must be unique - if ($value['value']['fid']!=$existing_feed['fid']) { - form_set_error($field['field_name'] .']['. $delta. '][value][feed', t('This feed already exists in the aggregator.')); - } - } elseif ($existing_feed=aggregation_get_feed_by_title($value['value']['title'])){ - // Titles must be unique - if ($value['value']['fid']!=$existing_feed['fid']) { - form_set_error($field['field_name'] .']['. $delta. '][value][title', t('A feed already exists with this title.')); - } - } - } - } - return; - - case 'process form values': - foreach($node_field as $delta => $value) { - //_feedfield_widget_process($node_field[$delta],$delta); - } - return; - - case 'submit': - return; - } -} - - - - /** - * Helper function renders the feedfield widget in both single and multiple value cases. + * Process the link type element before displaying the field. + * + * Build the form element. When creating a form using FAPI #process, + * note that $element['#value'] is already set. + * + * The $fields array is in $form['#field_info'][$element['#field_name']]. */ +function feedfield_process($element, $edit, $form_state, $form) { + $field = $form['#field_info'][$element['#field_name']]; + $delta = $element['#delta']; -function _feedfield_widget_form (&$form_item, $field, $node_field, $delta = 0) { - $form_item['value'] = array( - '#tree' => true, - '#weigth' => $field['widget']['weight'], - ); - - $form_item['value']['title'] = array( + $element['title'] = array( '#type' => 'textfield', '#maxlength' => '255', '#title' => t('Feed title'), - '#default_value' => $node_field['value']['title'], - ); - - $options = array ( - 'news' => t('News/blog feed'), - 'wiki' => t('Wiki recent changes'), - ); - - $form_item['value']['type'] = array( - '#type' => 'select', - '#title' => t('Feed type'), - '#default_value' => isset($node_field['value']['type']) ? $node_field['value']['type'] : 'news', - '#options' => $options, + '#default_value' => isset($element['#value']['value']['title']) ? + $element['#value']['value']['title'] : NULL, ); - $form_item['value']['feed'] = array( + $element['feed'] = array( '#type' => 'textfield', '#maxlength' => '255', '#title' => t(' Feed URL'), - '#default_value' => $node_field['value']['feed'], + '#default_value' => isset($element['#value']['value']['feed']) ? + $element['#value']['value']['feed'] : NULL, '#required' => ($delta == 0) ? $field['required'] : FALSE, - '#description' => $field['widget']['description'], ); - - $form_item['value']['category'] = array( + $element['category'] = array( '#type' => 'checkboxes', '#title' => t('Aggregator Categories'), '#options' => aggregation_get_all_feed_categories(), ); - ($node_field['value']['fid']) and $form_item['value']['category']['#default_value']=aggregation_get_assigned_feed_categories($node_field['value']['fid']); - - $form_item['value']['fid'] = array( + $element['fid'] = array( '#type' => 'hidden', - '#value' => $node_field['value']['fid'], + '#value' => isset($element['#value']['value']['fid']) ? + $element['#value']['value']['fid'] : NULL, ); + return $element; } /** - * Implementation of hook_field_view() which performs any translation necessary. + * Implementation of hook_field_formatter_info(). */ -function _feedfield_field_view($field, $value, $addlfields = array(), $node = NULL) { - $attributes = array(); - $output = l(strlen($value['feed']) > 80 ? substr($value['feed'],0,80)."..." : $value['feed'],feedfield_cleanup_url($value['feed']),$attributes); - return $output; -} - -/** - * Implementation of hook_views_tables(). - */ -function feedfield_views_tables() { - $tables = array(); - - $fields = content_fields(); - foreach ($fields as $field) { - if ($field['type'] == 'feedfield') { - $tables['node_field_feedfield_data_'. $field['field_name']] = array( - 'name' => 'node_field_feedfield_data', - 'join' => array( - 'left' => array( - 'table' => 'node', - 'field' => 'vid', - ), - 'right' => array( - 'field' => 'vid', - ), - 'extra' => array('field_name' => $field['field_name']), - ), - 'fields' => array( - 'field_url' => array( - 'name' => 'Feed URL: '. $field['widget']['label'], - 'sortable' => TRUE, - ), - 'field_title' => array( - 'name' => 'Feed title: '.$field['widget']['label'], - 'sortable' => TRUE, - ), - 'field_type' => array( - 'name' => 'Feed type: '. $field['widget']['label'], - 'sortable' => TRUE, - ), - ), - 'sorts' => array( - 'field_url' => array('name' => 'Feed URL: '. $field['widget']['label']), - 'field_title' => array('name' => 'Feed title: '. $field['widget']['label']), - 'field_type' => array('name' => 'Feed type: '. $field['widget']['label']), - ), - 'filters' => array( - 'field_url' => array( - 'name' => 'Feed URL: '. $field['widget']['label'], - 'operator' => array( - '=' => 'is', - 'contains' => 'contains', - 'begins' => 'begins with', - 'ends' => 'ends wth', - 'LIKE' => 'matches pattern', - ), - 'operator-handler' => '_text_filter_operator', - ), - 'field_title' => array( - 'name' => 'Feed title: '. $field['widget']['label'], - 'operator' => array( - '=' => 'is', - 'contains' => 'contains', - 'begins' => 'begins with', - 'ends' => 'ends wth', - 'LIKE' => 'matches pattern', - ), - 'operator-handler' => '_text_filter_operator', - ), - 'field_type' => array( - 'name' => 'Feed type: '. $field['widget']['label'], - 'operator' => "views_handler_operator_eqneq", - 'list' => array('news'=>'news','wiki'=>'wiki'), +function feedfield_field_formatter_info() { + return array( + 'default' => array( + 'label' => t('Display Aggregator Block (default)'), + 'field types' => array('feedfield'), + 'multiple values' => CONTENT_HANDLE_CORE, ), - ), ); } - } - - return $tables; -} /** - * Forms a valid URL if possible from an entered address. - * Trims whitespace and automatically adds an http:// to addresses without a protocol specified + * FAPI theme for an individual text elements. * - * @param string $url - * @param string $protocol The protocol to be prepended to the url if one is not specified - */ -function feedfield_cleanup_url ($url, $protocol = "http") { - $url = trim($url); - - // Check if there is no protocol specified - $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i",$url); - if (empty($protocol_match)) { - // But should it be? Add an automatic http:// if it starts with a domain name - $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)(aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|[a-z]{2}))/i',$url); - if (!empty($domain_match)) { - $url = $protocol."://".$url; - } - } - - return $url; -} - -/** - * A lenient verification for URLs. Accepts all URLs following RFC 1738 standard for URL formation. + * The textfield or textarea is already rendered by the + * textfield or textarea themes and the html output + * lives in $element['#children']. Override this theme to + * make custom changes to the output. * - * @param string $text - * @return mixed Returns boolean FALSE if the URL is not valid. On success, returns an object with - * the following attributes: protocol, hostname, ip, and port. - */ -function feedfield_validate_link($text) { - if (!preg_match( - // protocol - '/^([a-z0-9][a-z0-9\.\-_]*:\/\/)?'. - '('. - // domains - '(([a-z0-9]([a-z0-9\-_]*\.)+)(aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|[a-z]{2}))'. - // OR ip addresses - '|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'. - ')'. - // port number - '(:([0-9]{1,4}))?'. - // the rest of the path - "(\/[a-z0-9_\-\.~+%=&,$'():;*@]+)*". - // anchors - "\/?#?[a-z0-9_\-\.~+%=&,$'():;*@]*". - // the query string - "(\/?\?[a-z0-9+_\-\.\/%=&,$'():;*@]*)?". - // forward slash 0 or 1 times - '(\/)?'. - // end of the expression, case insensitive - '$/i', $text, $m)) { - return false; - } - else { - $url = new stdClass(); - $url->protocol = $m[2]; - $url->hostname = strtolower($m[5]).strtolower($m[7]); - $url->ip = $m[8]; - $url->port = $m[10]; - return $url; - } -} - - -/** - * Implementation of hook_block. - * + * $element['#field_name'] contains the field name + * $element['#delta] is the position of this element in the group */ -function feedfield_block($op = 'list', $delta = 0, $edit = array()) { - switch ($op){ - case 'list': - $fields=content_fields(); - if (is_array($fields)) { - foreach($fields as $field) { - if ($field['type']=='feedfield') { - $blocks[$field['field_name']]['info']=t("Feed for current node's @name feedfield",array('@name'=>$field['field_name'])); - } - } - } - return $blocks; - break; - case 'view': - if (arg(0)=='node' && is_numeric(arg(1))) { - $node=node_load(arg(1)); - $feeds=$node->$delta; - return aggregator_block('view', 'feed-'.$feeds[0]['value']['fid']); - } - break; +function theme_feedfield($element) { + return $element['#children']; } -} - /** - * Theme the way a feed title is displayed - * - * @return string with formatted title - */ -function theme_feedfield_title($node,$field,$item) { - if (!$result=$item['value']['title']) { - $result= $node->title.' ('.$item['value']['type'].')'; - } - return $result; -} \ No newline at end of file + * Theme function for default formatter. This is just a quick and dirty way of + * showing feeds with the existing aggregator_block method. Will want to + * replace with a proper theme eventually. + * + * @param array $element + * @return array + * @todo replace with a proper theme. + */ +function theme_feedfield_formatter_default($element) { + $fid = 'feed-'.$element['#item']['value']['fid']; + $show = aggregator_block('view', $fid); + return $show['content']; + } \ No newline at end of file