From 725e6aeb96ce77e47483f0a63eb3d1b3bcd45475 Mon Sep 17 00:00:00 2001 From: Alex Barth <alex_b@53995.no-reply.drupal.org> Date: Wed, 29 Sep 2010 23:26:19 +0000 Subject: [PATCH] Merge in changes from http://github.com/lxbarth/Feeds/commits/DRUPAL-7--1-0-alpha1. --- CHANGELOG.txt | 12 +- feeds.api.php | 91 +--- feeds.info | 3 + feeds.module | 212 +++----- feeds.pages.inc | 15 +- feeds_news/feeds_news.features.inc | 12 +- .../feeds_news.feeds_importer_default.inc | 30 +- feeds_news/feeds_news.info | 7 +- feeds_news/feeds_news.views_default.inc | 504 ++++++------------ feeds_ui/feeds_ui.admin.inc | 11 +- includes/FeedsBatch.inc | 21 +- includes/FeedsConfigurable.inc | 2 +- includes/FeedsImporter.inc | 8 +- includes/FeedsSource.inc | 20 +- libraries/http_request.inc | 2 +- mappers/content.inc | 59 -- mappers/field.inc | 71 +++ mappers/file.inc | 86 +++ mappers/filefield.inc | 79 --- mappers/taxonomy.inc | 170 +++--- plugins/FeedsFileFetcher.inc | 64 ++- plugins/FeedsNodeProcessor.inc | 13 +- plugins/FeedsParser.inc | 77 +-- plugins/FeedsPlugin.inc | 109 +++- plugins/FeedsProcessor.inc | 11 +- plugins/FeedsSimplePieParser.inc | 2 +- plugins/FeedsTermProcessor.inc | 69 ++- plugins/FeedsUserProcessor.inc | 3 +- tests/feeds.test.inc | 41 +- tests/feeds_processor_node.test | 165 +++--- tests/feeds_processor_term.test | 13 +- 31 files changed, 900 insertions(+), 1082 deletions(-) delete mode 100644 mappers/content.inc create mode 100644 mappers/field.inc create mode 100644 mappers/file.inc delete mode 100644 mappers/filefield.inc diff --git a/CHANGELOG.txt b/CHANGELOG.txt index e9a55794..c1dfb31b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,7 +3,17 @@ Feeds 7.x XXXXXXXXXXXXXXXXXXXXXX -------------------------------- - +- Expire files returned by FeedsImportBatch after DRUPAL_MAXIMUM_TEMP_FILE_AGE + seconds. +- FeedsFileFetcher: track uploaded files, delete unused files. +- yhahn: Upgrade FeedsTaxonomyProcessor. +- Remove handling of target items that are array. All target items must be + objects now. +- Upgrade file and image mapper. +- Upgrade taxonomy mapper. +- Upgrade field mapper. +- Move plugin handling into FeedsPlugin class. +- Base level upgrade. Feeds 6.x 1.0 Beta 6, 2010-09-16 -------------------------------- diff --git a/feeds.api.php b/feeds.api.php index 2d250dac..1a4640c8 100644 --- a/feeds.api.php +++ b/feeds.api.php @@ -151,7 +151,7 @@ function hook_feeds_parser_sources_alter(&$sources, $content_type) { } /** - * Callback specified in hook_feeds_parser_sources_alter(). + * Example callback specified in hook_feeds_parser_sources_alter(). * * To be invoked on mapping time. * @@ -173,25 +173,7 @@ function my_source_get_source(FeedsImportBatch $batch, $key) { } /** - * Alter mapping targets for users. Use this hook to add additional target - * options to the mapping form of User processors. - * - * For an example implementation, see mappers/profile.inc - * - * @param: &$targets - * Array containing the targets to be offered to the user. Add to this array - * to expose additional options. Remove from this array to suppress options. - */ -function hook_feeds_user_processor_targets_alter(&$targets) { - $targets['my_user_field'] = array( - 'name' => t('My custom user field'), - 'description' => t('Description of what my custom user field does.'), - 'callback' => 'my_callback', - ); -} - -/** - * Alter mapping targets for nodes. Use this hook to add additional target + * Alter mapping targets for entities. Use this hook to add additional target * options to the mapping form of Node processors. * * If the key in $targets[] does not correspond to the actual key on the node @@ -203,61 +185,40 @@ function hook_feeds_user_processor_targets_alter(&$targets) { * Array containing the targets to be offered to the user. Add to this array * to expose additional options. Remove from this array to suppress options. * Remove with caution. + * @param $entity_type + * The entity type of the target, for instance a 'node' entity. * @param $content_type * The content type of the target node. */ -function hook_feeds_node_processor_targets_alter(&$targets, $content_type) { - $targets['my_node_field'] = array( - 'name' => t('My custom node field'), - 'description' => t('Description of what my custom node field does.'), - 'callback' => 'my_callback', - ); - $targets['my_node_field2'] = array( - 'name' => t('My Second custom node field'), - 'description' => t('Description of what my second custom node field does.'), - 'callback' => 'my_callback2', - 'real_target' => 'my_node_field_two', // Specify real target field on node. - ); -} - -/** - * Alter mapping targets for taxonomy terms. Use this hook to add additional - * target options to the mapping form of Taxonomy term processor. - * - * For an example implementation, look at geotaxnomy module. - * http://drupal.org/project/geotaxonomy - * - * @param &$targets - * Array containing the targets to be offered to the user. Add to this array - * to expose additional options. Remove from this array to suppress options. - * Remove with caution. - * @param $vid - * The vocabulary id - */ -function hook_feeds_term_processor_targets_alter(&$targets, $vid) { - if (variable_get('mymodule_vocabulary_'. $vid, 0)) { - $targets['lat'] = array( - 'name' => t('Latitude'), - 'description' => t('Latitude of the term.'), +function hook_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) { + if ($entity_type == 'node') { + $targets['my_node_field'] = array( + 'name' => t('My custom node field'), + 'description' => t('Description of what my custom node field does.'), + 'callback' => 'my_module_set_target', ); - $targets['lon'] = array( - 'name' => t('Longitude'), - 'description' => t('Longitude of the term.'), + $targets['my_node_field2'] = array( + 'name' => t('My Second custom node field'), + 'description' => t('Description of what my second custom node field does.'), + 'callback' => 'my_module_set_target2', + 'real_target' => 'my_node_field_two', // Specify real target field on node. ); } } /** - * Alter mapping targets for Data table entries. Use this hook to add additional - * target options to the mapping form of Data processor. + * Example callback specified in hook_feeds_processor_targets_alter(). + * + * @param $entity + * An entity object, for instance a node object. + * @param $target + * A string identifying the target on the node. + * @param $value + * The value to populate the target with. + * */ -function hook_feeds_data_processor_targets_alter(&$fields, $data_table) { - if ($data_table == mymodule_base_table()) { - $fields['mytable:category'] = array( - 'name' => t('Category'), - 'description' => t('One or more category terms.'), - ); - } +function my_module_set_target($entity, $target, $value) { + $entity->$target['und'][0]['value'] = $value; } /** diff --git a/feeds.info b/feeds.info index 4d9e9b98..b34fc115 100644 --- a/feeds.info +++ b/feeds.info @@ -10,5 +10,8 @@ files[] = includes/FeedsBatch.inc files[] = includes/FeedsConfigurable.inc files[] = includes/FeedsImporter.inc files[] = includes/FeedsSource.inc +files[] = tests/feeds.test.inc +files[] = tests/feeds_processor_node.test +files[] = tests/feeds_processor_term.test core = 7.x php = 5.2 diff --git a/feeds.module b/feeds.module index 4008031a..871fce7d 100644 --- a/feeds.module +++ b/feeds.module @@ -151,7 +151,7 @@ function feeds_permission() { function feeds_forms() { $forms = array(); $forms['FeedsImporter_feeds_form']['callback'] = 'feeds_form'; - $plugins = feeds_get_plugins(); + $plugins = FeedsPlugin::all(); foreach ($plugins as $plugin) { $forms[$plugin['handler']['class'] .'_feeds_form']['callback'] = 'feeds_form'; } @@ -348,7 +348,9 @@ function feeds_node_validate($node, $form, &$form_state) { // $node->nid at this stage. $source = feeds_source($importer_id); - // Node module magically moved $form['feeds'] to $node->feeds :P + // Node module magically moved $form['feeds'] to $node->feeds :P. + // configFormValidate may modify $last_feed, smuggle it to update/insert stage + // through a static variable. $last_feeds = $node->feeds; $source->configFormValidate($last_feeds); @@ -367,12 +369,38 @@ function feeds_node_validate($node, $form, &$form_state) { } } +/** + * Implements hook_node_presave(). + */ +function feeds_node_presave($node) { + // Populate $node->title and $node->feed from result of validation phase. + $last_title = &drupal_static('feeds_node_last_title'); + $last_feeds = &drupal_static('feeds_node_last_feeds'); + if (empty($node->title) && !empty($last_title)) { + $node->title = $last_title; + } + if (!empty($last_feeds)) { + $node->feeds = $last_feeds; + } + $last_title = NULL; + $last_feeds = NULL; +} + /** * Implements hook_node_insert(). */ function feeds_node_insert($node) { _feeds_node_processor_node_insert($node); feeds_node_update($node); + if ($importer_id = feeds_get_importer_id($node->type)) { + // Start import if requested. + if (feeds_importer($importer_id)->config['import_on_create'] && !isset($node->feeds['suppress_import'])) { + feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid); + } + // Schedule source and importer. + feeds_source($importer_id, $node->nid)->schedule(); + feeds_importer($importer_id)->schedule(); + } } /** @@ -383,33 +411,10 @@ function feeds_node_update($node) { if (!$importer_id = feeds_get_importer_id($node->type)) { return; } - $last_title = &drupal_static('feeds_node_last_title'); - $last_feeds = &drupal_static('feeds_node_last_feeds'); - // Populate title from result of validation phase. - if (!empty($last_title)) { - $node->title = $last_title; - } - $last_title = NULL; - // A node may not have been validated, make sure $last_feeds is present. - if (empty($last_feeds)) { - $last_feeds = $node->feeds; - } // Add configuration to feed source and save. $source = feeds_source($importer_id, $node->nid); - $source->addConfig($last_feeds); + $source->addConfig($node->feeds); $source->save(); - - // Refresh feed if import on create is selected and suppress_import is - // not set. - if ($op == 'insert' && feeds_importer($importer_id)->config['import_on_create'] && !isset($last_feeds['suppress_import'])) { - feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid); - } - // Add source to schedule, make sure importer is scheduled, too. - if ($op == 'insert') { - $source->schedule(); - $source->importer->schedule(); - } - $last_feeds = NULL; } /** @@ -428,7 +433,7 @@ function feeds_node_delete($node) { function _feeds_node_processor_node_load($nodes) { if ($items = db_query("SELECT nid, imported, guid, url, feed_nid FROM {feeds_node_item} WHERE nid IN (:nids)", array(':nids' => array_keys($nodes)))) { foreach ($items as $item) { - $node->{$item->nid} = $item; + $nodes[$item->nid]->feeds_node_item = $item; } } } @@ -457,11 +462,9 @@ function _feeds_node_processor_node_update($node) { * FeedsNodeProcessor's hook_node_delete(). */ function _feeds_node_processor_node_delete($node) { - if (isset($node->feeds_node_item)) { - db_delete('feeds_node_item') - ->condition('nid', $node->nid) - ->execute(); - } + db_delete('feeds_node_item') + ->condition('nid', $node->nid) + ->execute(); } /** @@ -483,6 +486,7 @@ function feeds_taxonomy_term_insert($term) { $record = array( 'id' => $term->feeds_importer_id, 'tid' => $term->tid, + 'feed_nid' => $term->feed_nid, ); drupal_write_record('feeds_term_item', $record); } @@ -501,7 +505,7 @@ function feeds_taxonomy_term_delete($term) { * Implements hook_form_alter(). */ function feeds_form_alter(&$form, $form_state, $form_id) { - if ($form['#id'] == 'node-form') { + if (!empty($form['#node_edit_form'])) { if ($importer_id = feeds_get_importer_id($form['type']['#value'])) { // Set title to not required, try to retrieve it from feed. $form['title']['#required'] = FALSE; @@ -736,72 +740,9 @@ function feeds_source($importer_id, $feed_nid = 0) { return FeedsSource::instance($importer_id, $feed_nid); } -/** - * @} - */ - -/** - * @defgroup plugins Plugin functions - * @{ - * - * @todo Encapsulate this in a FeedsPluginHandler class, move it to includes/ - * and only load it if we're manipulating plugins. - */ - -/** - * Gets all available plugins. Does not list hidden plugins. - * - * @return - * An array where the keys are the plugin keys and the values - * are the plugin info arrays as defined in hook_feeds_plugins(). - */ -function feeds_get_plugins() { - ctools_include('plugins'); - $plugins = ctools_get_plugins('feeds', 'plugins'); - - $result = array(); - foreach ($plugins as $key => $info) { - if (!empty($info['hidden'])) { - continue; - } - $result[$key] = $info; - } - - // Sort plugins by name and return. - uasort($result, 'feeds_plugin_compare'); - return $result; -} - -/** - * Sort callback for feeds_get_plugins(). - */ -function feeds_plugin_compare($a, $b) { - return strcasecmp($a['name'], $b['name']); -} - -/** - * Gets all available plugins of a particular type. - * - * @param $type - * 'fetcher', 'parser' or 'processor' - */ -function feeds_get_plugins_by_type($type) { - $plugins = feeds_get_plugins(); - - $result = array(); - foreach ($plugins as $key => $info) { - if ($type == feeds_plugin_type($key)) { - $result[$key] = $info; - } - } - return $result; -} - /** * Gets an instance of a class for a given plugin and id. * - * @todo Collapse into FeedsPlugin::instance(). - * * @param $plugin * A string that is the key of the plugin to load. * @param $id @@ -813,7 +754,7 @@ function feeds_get_plugins_by_type($type) { * @throws Exception * If plugin can't be instantiated. */ -function feeds_plugin_instance($plugin, $id) { +function feeds_plugin($plugin, $id) { ctools_include('plugins'); if ($class = ctools_plugin_load_class('feeds', 'plugins', $plugin, 'handler')) { return FeedsConfigurable::instance($class, $id); @@ -830,60 +771,6 @@ function feeds_plugin_instance($plugin, $id) { return FeedsConfigurable::instance($class, $id); } -/** - * Determines whether given plugin is derived from given base plugin. - * - * @param $plugin_key - * String that identifies a Feeds plugin key. - * @param $parent_plugin - * String that identifies a Feeds plugin key to be tested against. - * - * @return - * TRUE if $parent_plugin is directly *or indirectly* a parent of $plugin, - * FALSE otherwise. - */ -function feeds_plugin_child($plugin_key, $parent_plugin) { - ctools_include('plugins'); - $plugins = ctools_get_plugins('feeds', 'plugins'); - $info = $plugins[$plugin_key]; - - if (empty($info['handler']['parent'])) { - return FALSE; - } - elseif ($info['handler']['parent'] == $parent_plugin) { - return TRUE; - } - else { - return feeds_plugin_child($info['handler']['parent'], $parent_plugin); - } -} - -/** - * Determines the type of a plugin. - * - * @param $plugin_key - * String that identifies a Feeds plugin key. - * - * @return - * One of the following values: - * 'fetcher' if the plugin is a fetcher - * 'parser' if the plugin is a parser - * 'processor' if the plugin is a processor - * FALSE otherwise. - */ -function feeds_plugin_type($plugin_key) { - if (feeds_plugin_child($plugin_key, 'FeedsFetcher')) { - return 'fetcher'; - } - elseif (feeds_plugin_child($plugin_key, 'FeedsParser')) { - return 'parser'; - } - elseif (feeds_plugin_child($plugin_key, 'FeedsProcessor')) { - return 'processor'; - } - return FALSE; -} - /** * @} */ @@ -938,6 +825,29 @@ function feeds_library_exists($file, $library) { return FALSE; } +/** + * Simplified drupal_alter(). + * + * - None of that 'multiple parameters by ref' crazyness. + * - Don't use module_implements() to allow hot including on behalf + * implementations (see mappers/). + */ +function feeds_alter($type, &$data) { + $args = array(&$data); + $additional_args = func_get_args(); + array_shift($additional_args); + array_shift($additional_args); + $args = array_merge($args, $additional_args); + + $list = module_list(); + foreach (module_list() as $module) { + $function = $module .'_'. $type .'_alter'; + if (function_exists($function)) { + call_user_func_array($function, $args); + } + } +} + /** * @} */ diff --git a/feeds.pages.inc b/feeds.pages.inc index 44b244ad..e58bd401 100644 --- a/feeds.pages.inc +++ b/feeds.pages.inc @@ -159,19 +159,18 @@ function theme_feeds_upload($variables) { _form_set_class($element, array('form-file')); $description = ''; if (!empty($element['#file_info'])) { - $info = $element['#file_info']; + $file = $element['#file_info']; + $wrapper = file_stream_wrapper_get_instance_by_uri($file->uri); $description .= '<div class="file-info">'; $description .= '<div class="file-name">'; - $description .= l(basename($info['path']), $info['path']); + $description .= l($file->filename, $wrapper->getExternalUrl()); $description .= '</div>'; $description .= '<div class="file-size">'; - $description .= format_size($info['size']); + $description .= format_size($file->filesize); + $description .= '</div>'; + $description .= '<div class="file-mime">'; + $description .= check_plain($file->filemime); $description .= '</div>'; - if (isset($info['mime'])) { - $description .= '<div class="file-mime">'; - $description .= check_plain($info['mime']); - $description .= '</div>'; - } $description .= '</div>'; } $description .= '<div class="file-upload">'; diff --git a/feeds_news/feeds_news.features.inc b/feeds_news/feeds_news.features.inc index 810c7621..1b7873f8 100644 --- a/feeds_news/feeds_news.features.inc +++ b/feeds_news/feeds_news.features.inc @@ -17,24 +17,18 @@ function feeds_news_node_info() { $items = array( 'feed' => array( 'name' => t('Feed'), - 'module' => 'node', + 'base' => 'node_content', 'description' => t('Subscribe to RSS or Atom feeds. Creates nodes of the content type "Feed item" from feed content.'), 'has_title' => '1', 'title_label' => t('Title'), - 'has_body' => '1', - 'body_label' => t('Body'), - 'min_word_count' => '0', 'help' => '', ), 'feed_item' => array( 'name' => t('Feed item'), - 'module' => 'node', + 'base' => 'node_content', 'description' => t('This content type is being used for automatically aggregated content from feeds.'), 'has_title' => '1', 'title_label' => t('Title'), - 'has_body' => '1', - 'body_label' => t('Body'), - 'min_word_count' => '0', 'help' => '', ), ); @@ -46,6 +40,6 @@ function feeds_news_node_info() { */ function feeds_news_views_api() { return array( - 'api' => '2', + 'api' => '3.0-alpha1', ); } diff --git a/feeds_news/feeds_news.feeds_importer_default.inc b/feeds_news/feeds_news.feeds_importer_default.inc index 91642ba4..4fe041c7 100644 --- a/feeds_news/feeds_news.feeds_importer_default.inc +++ b/feeds_news/feeds_news.feeds_importer_default.inc @@ -15,8 +15,8 @@ function feeds_news_feeds_importer_default() { 'fetcher' => array( 'plugin_key' => 'FeedsHTTPFetcher', 'config' => array( - 'auto_detect_feeds' => FALSE, - 'use_pubsubhubbub' => FALSE, + 'auto_detect_feeds' => 1, + 'use_pubsubhubbub' => 0, 'designated_hub' => '', ), ), @@ -31,30 +31,30 @@ function feeds_news_feeds_importer_default() { 'update_existing' => 0, 'expire' => '-1', 'mappings' => array( - '0' => array( + 0 => array( 'source' => 'title', 'target' => 'title', 'unique' => FALSE, ), - '1' => array( - 'source' => 'description', - 'target' => 'body', - 'unique' => FALSE, - ), - '2' => array( + 1 => array( 'source' => 'timestamp', 'target' => 'created', 'unique' => FALSE, ), - '3' => array( + 2 => array( 'source' => 'url', 'target' => 'url', - 'unique' => TRUE, + 'unique' => 1, ), - '4' => array( + 3 => array( 'source' => 'guid', 'target' => 'guid', - 'unique' => TRUE, + 'unique' => 1, + ), + 4 => array( + 'source' => 'description', + 'target' => 'field_body', + 'unique' => FALSE, ), ), 'input_format' => 0, @@ -92,12 +92,12 @@ function feeds_news_feeds_importer_default() { 'content_type' => 'feed', 'update_existing' => 0, 'mappings' => array( - '0' => array( + 0 => array( 'source' => 'title', 'target' => 'title', 'unique' => FALSE, ), - '1' => array( + 1 => array( 'source' => 'xmlurl', 'target' => 'source', 'unique' => 1, diff --git a/feeds_news/feeds_news.info b/feeds_news/feeds_news.info index c6f6d7d6..bc35836d 100644 --- a/feeds_news/feeds_news.info +++ b/feeds_news/feeds_news.info @@ -1,14 +1,17 @@ -core = "6.x" +core = "7.x" +dependencies[] = "features" dependencies[] = "feeds" dependencies[] = "views" description = "A news aggregator built with feeds, creates nodes from imported feed items. With OPML import." features[ctools][] = "feeds:feeds_importer_default:1" features[feeds_importer][] = "feed" features[feeds_importer][] = "opml" +features[field][] = "node-feed_item-field_body" features[node][] = "feed" features[node][] = "feed_item" features[views][] = "feeds_defaults_feed_items" -features[views_api][] = "api:2" +features[views_api][] = "api:3.0-alpha1" +files[] = "feeds_news.module" name = "Feeds News" package = "Feeds" project = "Feeds" diff --git a/feeds_news/feeds_news.views_default.inc b/feeds_news/feeds_news.views_default.inc index 62c68d40..97a10ee4 100644 --- a/feeds_news/feeds_news.views_default.inc +++ b/feeds_news/feeds_news.views_default.inc @@ -14,353 +14,169 @@ function feeds_news_views_default_views() { $view->view_php = ''; $view->base_table = 'node'; $view->is_cacheable = FALSE; - $view->api_version = 2; + $view->api_version = '3.0-alpha1'; $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ + +/* Display: Defaults */ $handler = $view->new_display('default', 'Defaults', 'default'); - $handler->override_option('relationships', array( - 'feed_nid' => array( - 'label' => 'Owner feed', - 'required' => 1, - 'id' => 'feed_nid', - 'table' => 'feeds_node_item', - 'field' => 'feed_nid', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - )); - $handler->override_option('fields', array( - 'url' => array( - 'label' => '', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'display_as_link' => 1, - 'exclude' => 1, - 'id' => 'url', - 'table' => 'feeds_node_item', - 'field' => 'url', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - 'title' => array( - 'label' => '', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 1, - 'path' => '[url]', - 'link_class' => '', - 'alt' => '', - 'prefix' => '<h2>', - 'suffix' => '</h2>', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 0, - 'exclude' => 0, - 'id' => 'title', - 'table' => 'node', - 'field' => 'title', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - 'title_1' => array( - 'label' => '', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 1, - 'exclude' => 1, - 'id' => 'title_1', - 'table' => 'node', - 'field' => 'title', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'feed_nid', - ), - 'nothing' => array( - 'label' => '', - 'alter' => array( - 'text' => 'From [title_1]', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'exclude' => 0, - 'id' => 'nothing', - 'table' => 'views', - 'field' => 'nothing', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - 'body' => array( - 'label' => '', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'exclude' => 0, - 'id' => 'body', - 'table' => 'node_revisions', - 'field' => 'body', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - 'edit_node' => array( - 'label' => '', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - 'strip_tags' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'text' => 'Edit', - 'exclude' => 0, - 'id' => 'edit_node', - 'table' => 'node', - 'field' => 'edit_node', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - )); - $handler->override_option('arguments', array( - 'nid' => array( - 'default_action' => 'empty', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'Articles from %1', - 'breadcrumb' => '', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'none', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'id' => 'nid', - 'table' => 'node', - 'field' => 'nid', - 'validate_user_argument_type' => 'uid', - 'validate_user_roles' => array( - '2' => 0, - ), - 'relationship' => 'feed_nid', - 'default_options_div_prefix' => '', - 'default_argument_fixed' => '', - 'default_argument_user' => 0, - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'feed' => 0, - 'feed_item' => 0, - 'article' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array(), - 'validate_argument_type' => 'tid', - 'validate_argument_transform' => 0, - 'validate_user_restrict_roles' => 0, - 'validate_argument_php' => '', - 'override' => array( - 'button' => 'Override', - ), - ), - )); - $handler->override_option('filters', array( - 'type' => array( - 'operator' => 'in', - 'value' => array( - 'feed_item' => 'feed_item', - ), - 'group' => '0', - 'exposed' => FALSE, - 'expose' => array( - 'operator' => FALSE, - 'label' => '', - ), - 'id' => 'type', - 'table' => 'node', - 'field' => 'type', - 'override' => array( - 'button' => 'Override', - ), - 'relationship' => 'none', - ), - )); - $handler->override_option('access', array( - 'type' => 'perm', - 'perm' => 'access content', - )); - $handler->override_option('cache', array( - 'type' => 'none', - )); - $handler->override_option('empty', 'There are no items for this feed at the moment.'); - $handler->override_option('empty_format', '1'); + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['style_plugin'] = 'default'; + $handler->display->display_options['row_plugin'] = 'fields'; + /* Empty text: Global: Text area */ + $handler->display->display_options['empty']['text']['id'] = 'area'; + $handler->display->display_options['empty']['text']['table'] = 'views'; + $handler->display->display_options['empty']['text']['field'] = 'area'; + $handler->display->display_options['empty']['text']['empty'] = FALSE; + $handler->display->display_options['empty']['text']['content'] = 'There are no items for this feed at the moment.'; + $handler->display->display_options['empty']['text']['format'] = '1'; + /* Relationship: Feeds Item: Owner feed */ + $handler->display->display_options['relationships']['feed_nid']['id'] = 'feed_nid'; + $handler->display->display_options['relationships']['feed_nid']['table'] = 'feeds_node_item'; + $handler->display->display_options['relationships']['feed_nid']['field'] = 'feed_nid'; + $handler->display->display_options['relationships']['feed_nid']['required'] = 1; + /* Field: Feeds Item: Item URL */ + $handler->display->display_options['fields']['url']['id'] = 'url'; + $handler->display->display_options['fields']['url']['table'] = 'feeds_node_item'; + $handler->display->display_options['fields']['url']['field'] = 'url'; + $handler->display->display_options['fields']['url']['label'] = ''; + $handler->display->display_options['fields']['url']['exclude'] = TRUE; + $handler->display->display_options['fields']['url']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['url']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['url']['alter']['trim'] = 0; + $handler->display->display_options['fields']['url']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['url']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['url']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['url']['alter']['html'] = 0; + $handler->display->display_options['fields']['url']['hide_empty'] = 0; + $handler->display->display_options['fields']['url']['empty_zero'] = 0; + $handler->display->display_options['fields']['url']['display_as_link'] = 1; + /* Field: Node: Title */ + $handler->display->display_options['fields']['title']['id'] = 'title'; + $handler->display->display_options['fields']['title']['table'] = 'node'; + $handler->display->display_options['fields']['title']['field'] = 'title'; + $handler->display->display_options['fields']['title']['label'] = ''; + $handler->display->display_options['fields']['title']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['title']['alter']['make_link'] = 1; + $handler->display->display_options['fields']['title']['alter']['path'] = '[url]'; + $handler->display->display_options['fields']['title']['alter']['prefix'] = '<h2>'; + $handler->display->display_options['fields']['title']['alter']['suffix'] = '</h2>'; + $handler->display->display_options['fields']['title']['alter']['trim'] = 0; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['title']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['title']['alter']['html'] = 0; + $handler->display->display_options['fields']['title']['hide_empty'] = 0; + $handler->display->display_options['fields']['title']['empty_zero'] = 0; + $handler->display->display_options['fields']['title']['link_to_node'] = 0; + /* Field: Node: Title */ + $handler->display->display_options['fields']['title_1']['id'] = 'title_1'; + $handler->display->display_options['fields']['title_1']['table'] = 'node'; + $handler->display->display_options['fields']['title_1']['field'] = 'title'; + $handler->display->display_options['fields']['title_1']['relationship'] = 'feed_nid'; + $handler->display->display_options['fields']['title_1']['label'] = ''; + $handler->display->display_options['fields']['title_1']['exclude'] = TRUE; + $handler->display->display_options['fields']['title_1']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['title_1']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['title_1']['alter']['trim'] = 0; + $handler->display->display_options['fields']['title_1']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['title_1']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['title_1']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['title_1']['alter']['html'] = 0; + $handler->display->display_options['fields']['title_1']['hide_empty'] = 0; + $handler->display->display_options['fields']['title_1']['empty_zero'] = 0; + $handler->display->display_options['fields']['title_1']['link_to_node'] = 1; + /* Field: Global: Custom text */ + $handler->display->display_options['fields']['nothing']['id'] = 'nothing'; + $handler->display->display_options['fields']['nothing']['table'] = 'views'; + $handler->display->display_options['fields']['nothing']['field'] = 'nothing'; + $handler->display->display_options['fields']['nothing']['label'] = ''; + $handler->display->display_options['fields']['nothing']['alter']['text'] = 'From [title_1]'; + $handler->display->display_options['fields']['nothing']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['nothing']['alter']['trim'] = 0; + $handler->display->display_options['fields']['nothing']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['nothing']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['nothing']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['nothing']['alter']['html'] = 0; + $handler->display->display_options['fields']['nothing']['hide_empty'] = 0; + $handler->display->display_options['fields']['nothing']['empty_zero'] = 0; + /* Field: Fields: field_body */ + $handler->display->display_options['fields']['entity_id']['id'] = 'entity_id'; + $handler->display->display_options['fields']['entity_id']['table'] = 'field_data_field_body'; + $handler->display->display_options['fields']['entity_id']['field'] = 'entity_id'; + $handler->display->display_options['fields']['entity_id']['label'] = ''; + $handler->display->display_options['fields']['entity_id']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['entity_id']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['entity_id']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['entity_id']['alter']['trim'] = 0; + $handler->display->display_options['fields']['entity_id']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['entity_id']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['entity_id']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['entity_id']['alter']['html'] = 0; + $handler->display->display_options['fields']['entity_id']['hide_empty'] = 1; + $handler->display->display_options['fields']['entity_id']['empty_zero'] = 0; + /* Field: Node: Edit link */ + $handler->display->display_options['fields']['edit_node']['id'] = 'edit_node'; + $handler->display->display_options['fields']['edit_node']['table'] = 'node'; + $handler->display->display_options['fields']['edit_node']['field'] = 'edit_node'; + $handler->display->display_options['fields']['edit_node']['label'] = ''; + $handler->display->display_options['fields']['edit_node']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['edit_node']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['edit_node']['alter']['trim'] = 0; + $handler->display->display_options['fields']['edit_node']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['edit_node']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['edit_node']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['edit_node']['alter']['html'] = 0; + $handler->display->display_options['fields']['edit_node']['hide_empty'] = 0; + $handler->display->display_options['fields']['edit_node']['empty_zero'] = 0; + $handler->display->display_options['fields']['edit_node']['text'] = 'Edit'; + /* Argument: Node: Nid */ + $handler->display->display_options['arguments']['nid']['id'] = 'nid'; + $handler->display->display_options['arguments']['nid']['table'] = 'node'; + $handler->display->display_options['arguments']['nid']['field'] = 'nid'; + $handler->display->display_options['arguments']['nid']['relationship'] = 'feed_nid'; + $handler->display->display_options['arguments']['nid']['default_action'] = 'empty'; + $handler->display->display_options['arguments']['nid']['style_plugin'] = 'default_summary'; + $handler->display->display_options['arguments']['nid']['title'] = 'Articles from %1'; + $handler->display->display_options['arguments']['nid']['default_argument_type'] = 'fixed'; + $handler->display->display_options['arguments']['nid']['break_phrase'] = 0; + $handler->display->display_options['arguments']['nid']['not'] = 0; + /* Filter: Node: Type */ + $handler->display->display_options['filters']['type']['id'] = 'type'; + $handler->display->display_options['filters']['type']['table'] = 'node'; + $handler->display->display_options['filters']['type']['field'] = 'type'; + $handler->display->display_options['filters']['type']['value'] = array( + 'feed_item' => 'feed_item', + ); + $handler->display->display_options['filters']['type']['expose']['operator'] = FALSE; + +/* Display: Page */ $handler = $view->new_display('page', 'Page', 'page_1'); - $handler->override_option('arguments', array( - 'nid' => array( - 'default_action' => 'empty', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'All items from %1', - 'breadcrumb' => '', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'node', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'id' => 'nid', - 'table' => 'node', - 'field' => 'nid', - 'validate_user_argument_type' => 'uid', - 'validate_user_roles' => array( - '2' => 0, - ), - 'relationship' => 'feed_nid', - 'default_options_div_prefix' => '', - 'default_argument_fixed' => '', - 'default_argument_user' => 0, - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'feed' => 'feed', - 'feed_item' => 0, - 'article' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array(), - 'validate_argument_type' => 'tid', - 'validate_argument_transform' => 0, - 'validate_user_restrict_roles' => 0, - 'validate_argument_php' => '', - 'override' => array( - 'button' => 'Use default', - ), - ), - )); - $handler->override_option('path', 'node/%/feed-items'); - $handler->override_option('menu', array( - 'type' => 'tab', - 'title' => 'View items', - 'description' => '', - 'weight' => '0', - 'name' => 'navigation', - )); - $handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, - 'name' => 'navigation', - )); + $handler->display->display_options['defaults']['arguments'] = FALSE; + /* Argument: Node: Nid */ + $handler->display->display_options['arguments']['nid']['id'] = 'nid'; + $handler->display->display_options['arguments']['nid']['table'] = 'node'; + $handler->display->display_options['arguments']['nid']['field'] = 'nid'; + $handler->display->display_options['arguments']['nid']['relationship'] = 'feed_nid'; + $handler->display->display_options['arguments']['nid']['default_action'] = 'empty'; + $handler->display->display_options['arguments']['nid']['style_plugin'] = 'default_summary'; + $handler->display->display_options['arguments']['nid']['title'] = 'All items from %1'; + $handler->display->display_options['arguments']['nid']['default_argument_type'] = 'fixed'; + $handler->display->display_options['arguments']['nid']['validate_type'] = 'node'; + $handler->display->display_options['arguments']['nid']['validate_options']['types'] = array( + 'feed' => 'feed', + 'feed_item' => 0, + 'article' => 0, + ); + $handler->display->display_options['arguments']['nid']['break_phrase'] = 0; + $handler->display->display_options['arguments']['nid']['not'] = 0; + $handler->display->display_options['path'] = 'node/%/feed-items'; + $handler->display->display_options['menu']['type'] = 'tab'; + $handler->display->display_options['menu']['title'] = 'View items'; + $handler->display->display_options['menu']['weight'] = '0'; $views[$view->name] = $view; diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc index 86baec9f..08eba6ee 100644 --- a/feeds_ui/feeds_ui.admin.inc +++ b/feeds_ui/feeds_ui.admin.inc @@ -273,7 +273,7 @@ function feeds_ui_export_form($form, &$form_state, $importer) { function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { // Get plugins and configuration. - $plugins = feeds_get_plugins(); + $plugins = FeedsPlugin::all(); $config = $importer->config; // Base path for changing the active container. $path = 'admin/structure/feeds/edit/'. $importer->id; @@ -301,9 +301,9 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { $active_container['title'] = t('Basic settings'); $active_container['body'] = feeds_get_form($importer, 'configForm'); } - // feeds_plugin_instance() returns a correct result because feed has been + // feeds_plugin() returns a correct result because feed has been // instantiated previously. - elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin_instance($plugin_key, $importer->id)) { + elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin($plugin_key, $importer->id)) { $active_container['title'] = t('Settings for !plugin', array('!plugin' => $plugins[$plugin_key]['name'])); $active_container['body'] = feeds_get_form($plugin, 'configForm'); } @@ -414,7 +414,7 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { * A Form API form definition. */ function feeds_ui_plugin_form($form, &$form_state, $importer, $type) { - $plugins = feeds_get_plugins_by_type($type); + $plugins = FeedsPlugin::byType($type); $form = array(); $form['#importer'] = $importer; @@ -788,9 +788,10 @@ function theme_feeds_ui_mapping_form($variables) { foreach ($form['#mappings'] as $i => $mapping) { // Some parsers do not define source options. $source = isset($form['source']['#options'][$mapping['source']]) ? $form['source']['#options'][$mapping['source']] : $mapping['source']; + $target = isset($form['target']['#options'][$mapping['target']]) ? check_plain($form['target']['#options'][$mapping['target']]) : '<em>' . t('Missing') . '</em>'; $rows[] = array( check_plain($source), - check_plain($form['target']['#options'][$mapping['target']]), + $target, drupal_render($form['unique_flags'][$i]), drupal_render($form['remove_flags'][$i]), ); diff --git a/includes/FeedsBatch.inc b/includes/FeedsBatch.inc index 59144c4b..0bd873b3 100644 --- a/includes/FeedsBatch.inc +++ b/includes/FeedsBatch.inc @@ -183,6 +183,11 @@ class FeedsImportBatch extends FeedsBatch { } /** + * Get a path to a temporary file containing the resource provided by the + * fetcher. + * + * File will be deleted after DRUPAL_MAXIMUM_TEMP_FILE_AGE. + * * @return * A path to a file containing the raw content as a source. * @@ -191,17 +196,19 @@ class FeedsImportBatch extends FeedsBatch { */ public function getFilePath() { if (!isset($this->file_path)) { - $dir = 'public://feeds'; - if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + $destination = 'public://feeds'; + if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { throw new Exception(t('Feeds directory either cannot be created or is not writable.')); } - $dest = file_destination($dir . '/' . get_class($this) .'_'. drupal_get_token($this->url) .'_'. time(), FILE_EXISTS_RENAME); $this->file_path = FALSE; - $file = file_save_data($this->getRaw(), $dest); - if ($file === FALSE) { - throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest))); + if ($file = file_save_data($this->getRaw(), $destination . '/'. get_class($this) . REQUEST_TIME)) { + $file->status = 0; + file_save($file); + $this->file_path = $file->uri; + } + else { + throw new Exception(t('Cannot write content to %dest', array('%dest' => $destination))); } - $this->file_path = $file->uri; } return $this->file_path; } diff --git a/includes/FeedsConfigurable.inc b/includes/FeedsConfigurable.inc index 6bef67b3..1eb76ed6 100644 --- a/includes/FeedsConfigurable.inc +++ b/includes/FeedsConfigurable.inc @@ -47,7 +47,7 @@ abstract class FeedsConfigurable { /** * Instantiate a FeedsConfigurable object. * - * Don't use directly, use feeds_importer() or feeds_plugin_instance() + * Don't use directly, use feeds_importer() or feeds_plugin() * instead. */ public static function instance($class, $id) { diff --git a/includes/FeedsImporter.inc b/includes/FeedsImporter.inc index 64b2554f..4ef67544 100644 --- a/includes/FeedsImporter.inc +++ b/includes/FeedsImporter.inc @@ -27,7 +27,7 @@ class FeedsImporter extends FeedsConfigurable { // Every feed has a fetcher, a parser and a processor. // These variable names match the possible return values of - // feeds_plugin_type(). + // FeedsPlugin::typeOf(). protected $fetcher, $parser, $processor; // This array defines the variable names of the plugins above. @@ -46,7 +46,7 @@ class FeedsImporter extends FeedsConfigurable { // Instantiate fetcher, parser and processor, set their configuration if // stored info is available. foreach ($this->plugin_types as $type) { - $plugin = feeds_plugin_instance($this->config[$type]['plugin_key'], $this->id); + $plugin = feeds_plugin($this->config[$type]['plugin_key'], $this->id); if (isset($this->config[$type]['config'])) { $plugin->setConfig($this->config[$type]['config']); @@ -155,8 +155,8 @@ class FeedsImporter extends FeedsConfigurable { */ public function setPlugin($plugin_key) { // $plugin_type can be either 'fetcher', 'parser' or 'processor' - if ($plugin_type = feeds_plugin_type($plugin_key)) { - if ($plugin = feeds_plugin_instance($plugin_key, $this->id)) { + if ($plugin_type = FeedsPlugin::typeOf($plugin_key)) { + if ($plugin = feeds_plugin($plugin_key, $this->id)) { // Unset existing plugin, switch to new plugin. unset($this->$plugin_type); $this->$plugin_type = $plugin; diff --git a/includes/FeedsSource.inc b/includes/FeedsSource.inc index 377936c8..8c1ad1ae 100644 --- a/includes/FeedsSource.inc +++ b/includes/FeedsSource.inc @@ -224,11 +224,12 @@ class FeedsSource extends FeedsConfigurable { * Save configuration. */ public function save() { - $config = $this->getConfig(); // Alert implementers of FeedsSourceInterface to the fact that we're saving. foreach ($this->importer->plugin_types as $type) { $this->importer->$type->sourceSave($this); } + $config = $this->getConfig(); + // Store the source property of the fetcher in a separate column so that we // can do fast lookups on it. $source = ''; @@ -307,7 +308,7 @@ class FeedsSource extends FeedsConfigurable { } /** - * Convenience function. Returns the configuration for a specific class. + * Returns the configuration for a specific client class. * * @param FeedsSourceInterface $client * An object that is an implementer of FeedsSourceInterface. @@ -319,6 +320,21 @@ class FeedsSource extends FeedsConfigurable { return $this->config[get_class($client)]; } + /** + * Sets the configuration for a specific client class. + * + * @param FeedsSourceInterface $client + * An object that is an implementer of FeedsSourceInterface. + * @param $config + * The configuration for $client. + * + * @return + * An array stored for $client. + */ + public function setConfigFor(FeedsSourceInterface $client, $config) { + $this->config[get_class($client)] = $config; + } + /** * Return defaults for feed configuration. */ diff --git a/libraries/http_request.inc b/libraries/http_request.inc index a9457247..33d663ad 100644 --- a/libraries/http_request.inc +++ b/libraries/http_request.inc @@ -130,7 +130,7 @@ function http_request_get($url, $username = NULL, $password = NULL, $accept_inva // https. // Validate in PHP, CURLOPT_PROTOCOLS is only supported with cURL 7.19.4 $uri = parse_url($url); - if ($uri['scheme'] != 'http' && $uri['scheme'] != 'https') { + if (isset($uri['scheme']) && $uri['scheme'] != 'http' && $uri['scheme'] != 'https') { $result->error = 'invalid schema '. $uri['scheme']; $result->code = -1003; // This corresponds to drupal_http_request() } diff --git a/mappers/content.inc b/mappers/content.inc deleted file mode 100644 index 416cfd10..00000000 --- a/mappers/content.inc +++ /dev/null @@ -1,59 +0,0 @@ -<?php -// $Id$ - -/** - * @file - * On behalf implementation of Feeds mapping API for content.module (CCK). - */ - -/** - * Implements hook_feeds_node_processor_targets_alter(). - * - * @see FeedsNodeProcessor::getMappingTargets(). - */ -function content_feeds_node_processor_targets_alter(&$targets, $content_type) { - $info = content_types($content_type); - $fields = array(); - if (isset($info['fields']) && count($info['fields'])) { - foreach ($info['fields'] as $field_name => $field) { - if (in_array($field['type'], array('text', 'number_integer', 'number_decimal', 'number_float'))) { - $fields[$field_name] = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name; - } - } - } - foreach ($fields as $k => $name) { - $targets[$k] = array( - 'name' => $name, - 'callback' => 'content_feeds_set_target', - 'description' => t('The CCK !name field of the node.', array('!name' => $name)), - ); - } -} - -/** - * Callback for mapping. Here is where the actual mapping happens. - * - * When the callback is invoked, $target contains the name of the field the - * user has decided to map to and $value contains the value of the feed item - * element the user has picked as a source. - */ -function content_feeds_set_target($node, $target, $value) { - - $field = isset($node->$target) ? $node->$target : array(); - - // Handle multiple value fields. - if (is_array($value)) { - $i = 0; - foreach ($value as $v) { - if (!is_array($v) && !is_object($v)) { - $field[$i]['value'] = $v; - } - $i++; - } - } - else { - $field[0]['value'] = $value; - } - - $node->$target = $field; -} diff --git a/mappers/field.inc b/mappers/field.inc new file mode 100644 index 00000000..40341646 --- /dev/null +++ b/mappers/field.inc @@ -0,0 +1,71 @@ +<?php +// $Id$ + +/** + * @file + * On behalf implementation of Feeds mapping API for field.module. + * + * Does actually not include mappers for field types defined in fields module + * (because there aren't any) but mappers for all fields that contain their + * value simply in $entity->fieldname['und'][$i]['value']. + */ + +/** + * Implements hook_feeds_processor_targets_alter(). + * + * @see FeedsNodeProcessor::getMappingTargets(). + */ +function field_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) { + foreach (field_info_instances($entity_type, $content_type) as $name => $instance) { + $info = field_info_field($name); + $allowed_types = array( + 'number_integer', + 'number_decimal', + 'number_float', + 'text', + 'text_long', + 'text_with_summary', + ); + if (in_array($info['type'], $allowed_types)) { + $targets[$name] = array( + 'name' => $instance['label'], + 'callback' => 'field_feeds_set_target', + 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), + ); + } + } +} + +/** + * Callback for mapping. Here is where the actual mapping happens. + * + * When the callback is invoked, $target contains the name of the field the + * user has decided to map to and $value contains the value of the feed item + * element the user has picked as a source. + */ +function field_feeds_set_target($entity, $target, $value) { + if (empty($value)) { + return; + } + + // Handle non-multiple value fields. + if (!is_array($value)) { + $value = array($value); + } + + $info = field_info_field($target); + + // Iterate over all values. + $i = 0; + $field = isset($entity->$target) ? $entity->$target : array(); + foreach ($value as $v) { + if (!is_array($v) && !is_object($v)) { + $field['und'][$i]['value'] = $v; + } + if ($info['cardinality'] == 1) { + break; + } + $i++; + } + $entity->{$target} = $field; +} diff --git a/mappers/file.inc b/mappers/file.inc new file mode 100644 index 00000000..bf21e980 --- /dev/null +++ b/mappers/file.inc @@ -0,0 +1,86 @@ +<?php +// $Id$ + +/** + * @file + * On behalf implementation of Feeds mapping API for file.module and + * image.module. + * + * Does actually not include mappers for field types defined in fields module + * (because there aren't any) but mappers for all fields that contain their + * value simply in $entity->fieldname['und'][$i]['value']. + */ + +/** + * Implements hook_feeds_processor_targets_alter(). + * + * @see FeedsNodeProcessor::getMappingTargets(). + */ +function file_feeds_processor_targets_alter(&$targets, $entity_type, $content_type = '') { + foreach (field_info_instances($entity_type, $content_type) as $name => $instance) { + $info = field_info_field($name); + + if (in_array($info['type'], array('file', 'image'))) { + $targets[$name] = array( + 'name' => $instance['label'], + 'callback' => 'file_feeds_set_target', + 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), + ); + } + } +} + +/** + * Callback for mapping. Here is where the actual mapping happens. + * + * When the callback is invoked, $target contains the name of the field the + * user has decided to map to and $value contains the value of the feed item + * element the user has picked as a source. + */ +function file_feeds_set_target($entity, $target, $value) { + if (empty($value)) { + return; + } + module_load_include('inc', 'file'); + + // Make sure $value is an array of objects of type FeedsEnclosure. + if (!is_array($value)) { + $value = array($value); + } + foreach ($value as $k => $v) { + if (!($v instanceof FeedsEnclosure)) { + if (is_string($v)) { + $value[$k] = new FeedsEnclosure($v, 'application/octet-stream'); + } + else { + unset($value[$k]); + } + } + } + if (empty($value)) { + return; + } + + // Determine file destination. + // @todo This needs review and debugging. + $info = field_info_field($target); + $bundle_name = $entity->entity_type == 'node' ? $entity->type : $entity->entity_type; + $instance_info = field_info_instance($entity->entity_type, $target, $bundle_name); + $account = $entity->uid ? user_load($entity->uid) : NULL; + $destination = file_field_widget_uri($info, $instance_info, $account); + + // Populate entity. + $i = 0; + $field = isset($entity->$target) ? $entity->$target : array(); + foreach ($value as $v) { + if ($file = $v->getFile($destination)) { + $field['und'][$i] = (array)$file; + $field['und'][$i]['display'] = 1; // @todo: Figure out how to properly populate this field. + if ($info['cardinality'] == 1) { + break; + } + $i++; + } + } + $entity->{$target} = $field; +} diff --git a/mappers/filefield.inc b/mappers/filefield.inc deleted file mode 100644 index 82e11bb7..00000000 --- a/mappers/filefield.inc +++ /dev/null @@ -1,79 +0,0 @@ -<?php -// $Id$ - -/** - * @file - * On behalf implementation of Feeds mapping API for filefield.module (CCK). - * - * @todo Support <em>list</em> subfields (how to provide default value?) - * @todo Support <em>data</em> subfields (or its own subfieds?) - */ - -/** - * Implements hook_feeds_node_processor_targets_alter() - */ -function filefield_feeds_node_processor_targets_alter(&$targets, $content_type) { - $info = content_types($content_type); - $fields = array(); - if (isset($info['fields']) && count($info['fields'])) { - foreach ($info['fields'] as $field_name => $field) { - if ($field['type'] == 'filefield') { - $name = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name; - $targets[$field_name] = array( - 'name' => $name, - 'callback' => 'filefield_feeds_set_target', - 'description' => t('The URL for the CCK @name field of the node.', array('@name' => $name)), - ); - } - } - } -} - -/** - * Implements hook_feeds_set_target(). - * - * @param $node - * The target node. - * @param $field_name - * The name of field on the target node to map to. - * @param $value - * The value to be mapped. Should contain a URL or an array of URLs; a - * FeedsEnclosure or an array of FeedsEnclosures. - * - * @todo: should we support $object->url again? - */ -function filefield_feeds_set_target($node, $field_name, $value) { - // Normalize $value, create an array of FeedsEnclosures of it. - $enclosures = array(); - if (!is_array($value)) { - $value = array($value); - } - foreach ($value as $k => $v) { - if ($v instanceof FeedsEnclosure) { - $enclosures[] = $v; - } - elseif (valid_url($v)) { - $enclosures[$k] = new FeedsEnclosure($v, 'application/octet-stream'); - } - } - - // Map enclosures. - $items = isset($node->$field_name) ? $node->$field_name : array(); - foreach ($enclosures as $enclosure) { - if ($file = $enclosure->getFile()) { - $field = content_fields($field_name, $node->type); - $target_dir = filefield_widget_file_path($field, user_load($node->uid)); - $info = field_file_save_file($enclosure->getFile(), array(), $target_dir); - if ($info) { - $info['list'] = array(); - $info['data'] = array('description' => ''); - if ($field['list_field']) { - $info['list'] = $field['list_default']; - } - $items[] = $info; - $error = false; - } - } - } - $node->$field_name = $items; -} diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc index 92405c51..1b3a115a 100644 --- a/mappers/taxonomy.inc +++ b/mappers/taxonomy.inc @@ -12,7 +12,7 @@ function taxonomy_feeds_parser_sources_alter(&$sources, $content_type) { if (!empty($content_type)) { foreach (taxonomy_get_vocabularies($content_type) as $vocabulary) { - $sources['parent:taxonomy:'. taxonomy_vocabulary_id($vocabulary)] = array( + $sources['parent:taxonomy:'. $vocabulary->machine_name] = array( 'name' => t('Feed node: Taxonomy: @vocabulary', array('@vocabulary' => $vocabulary->name)), 'description' => t('Taxonomy terms from feed node in given vocabulary.'), 'callback' => 'taxonomy_feeds_get_source', @@ -27,7 +27,8 @@ function taxonomy_feeds_parser_sources_alter(&$sources, $content_type) { function taxonomy_feeds_get_source(FeedsImportBatch $batch, $key) { if ($node = $batch->feedNode()) { $terms = taxonomy_node_get_terms($node); - $vocabulary = taxonomy_get_vocabulary(str_replace('parent:taxonomy:', '', $key)); + $vocabularies = taxonomy_vocabulary_load_multiple(array(), array('machine_name' => str_replace('parent:taxonomy:', '', $key))); + $vocabulary = array_shift($vocabularies); $result = array(); foreach ($terms as $tid => $term) { if ($term->vid == $vocabulary->vid) { @@ -39,142 +40,91 @@ function taxonomy_feeds_get_source(FeedsImportBatch $batch, $key) { } /** - * Implements hook_feeds_node_processor_targets_alter(). - * - * @see FeedsNodeProcessor::getMappingTargets(). + * Implements hook_feeds_processor_targets_alter(). */ -function taxonomy_feeds_node_processor_targets_alter(&$targets, $content_type) { - foreach (taxonomy_get_vocabularies($content_type) as $vocabulary) { - $description = t('The @name vocabulary of the node. If this is a "Tags" vocabulary, any new terms will be created on import. Otherwise only existing terms will be used. If this is not a "Tags" vocabulary and not a "Multiple select" vocabulary, only the first available term will be created. See !settings.', array('@name' => $vocabulary->name, '!settings' => l(t('vocabulary settings'), 'admin/content/taxonomy/edit/vocabulary/'. $vocabulary->vid, array('query' => 'destination='. $_GET['q'])))); - - $targets['taxonomy:'. taxonomy_vocabulary_id($vocabulary)] = array( - 'name' => "Taxonomy: ". $vocabulary->name, - 'callback' => 'taxonomy_feeds_set_target', - 'description' => $description, - 'real_target' => 'taxonomy', - ); +function taxonomy_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) { + foreach (field_info_instances($entity_type, $content_type) as $name => $instance) { + $info = field_info_field($name); + if ($info['type'] == 'taxonomy_term_reference') { + $targets[$name] = array( + 'name' => $instance['label'], + 'callback' => 'taxonomy_feeds_set_target', + 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), + ); + } } } /** * Callback for mapping. Here is where the actual mapping happens. * - * @param $node - * Reference to the node object we are working on. - * - * @param $key - * A key as added to the $targets array by - * taxonomy_feeds_node_processor_targets_alter(). - * - * @param $terms - * Given terms as array. - * - * Add the given terms to the node object so the taxonomy module can add them - * on node_save(). + * @todo Do not create new terms for non-autotag fields. */ -function taxonomy_feeds_set_target(&$node, $key, $terms) { - - // Return if there are no terms. +function taxonomy_feeds_set_target($entity, $target, $terms) { if (empty($terms)) { return; } - // Load target vocabulary to check, if it has the "tags" flag. - $vocabulary = taxonomy_get_vocabulary(str_replace('taxonomy:', '', $key)); - - // Cast a given single string to an array so we can use it. + // Handle non-multiple values. if (!is_array($terms)) { $terms = array($terms); } - if ($vocabulary->tags) { - // Simply add a comma separated list to the node for a "tags" vocabulary. - $terms = array_merge($terms, drupal_explode_tags($node->taxonomy['tags'][$vocabulary->vid])); - $node->taxonomy['tags'][$vocabulary->vid] = implode(',', $terms); + $info = field_info_field($target); + + // See http://drupal.org/node/881530 + if (isset($info['settings']['allowed_values'][0]['vocabulary'])) { + $vocabulary = taxonomy_vocabulary_machine_name_load($info['settings']['allowed_values'][0]['vocabulary']); } else { - foreach ($terms as $term) { - if ($term instanceof FeedsTermElement) { - $node->taxonomy[$term->tid] = (object)$term; - } - // Check if a term already exists. - elseif ($terms_found = taxonomy_get_term_by_name_vid($term, $vocabulary->vid)) { - // If any terms are found add them to the node's taxonomy by found tid. - foreach ($terms_found AS $term_found) { - $node->taxonomy[$term_found->tid] = $term_found; - if (!$vocabulary->multiple) { - break; - } - } - } - // If the vocab is not for multiple tags break after the first hit. - if (!$vocabulary->multiple) { - break; - } - } + $vocabulary = taxonomy_vocabulary_load($info['settings']['allowed_values'][0]['vid']); } -} -/** - * Try to map a string to an existing term by name and vocabulary id. - * - * Provides a case-insensitive and trimmed mapping, to maximize the likelihood - * of a successful match limited by a vocabulary id. - * - * @param $name - * Name of the term to search for. - * - * @param $vid - * The vocabulary's ID. - * - * @return - * An array of matching term objects. - */ -function taxonomy_get_term_by_name_vid($name, $vid) { - $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE LOWER(t.name) = LOWER('%s') AND t.vid = %d", 't', 'tid'), trim($name), $vid); - $result = array(); - while ($term = db_fetch_object($db_result)) { - $result[] = $term; - } - return $result; -} + $i = 0; + $entity->$target = isset($entity->$target) ? $entity->$target : array(); + foreach ($terms as $term) { + $tid = 0; + if ($term instanceof FeedsTermElement) { + $tid = $term->tid; + } + elseif (is_numeric($term)) { + $tid = $term; + } + elseif (is_string($term)) { + $tid = taxonomy_term_check_term($term, $vocabulary->vid); + } + if ($tid) { + $entity->{$target}['und'][$i]['tid'] = $tid; + } -/** - * Look up a vocabulary by vid or module name. - * - * @param $id - * A module name or a numeric vocabulary id. - * - * @return - * An object of type stdClass that represents a vocabulary. - */ -function taxonomy_get_vocabulary($id) { - static $vocabularies; - if (!isset($vocabularies[$id])) { - foreach (taxonomy_get_vocabularies() as $vocabulary) { - if ($vocabulary->vid == $id) { - $vocabularies[$id] = $vocabulary; - break; - } - elseif ($vocabulary->module == $id) { - $vocabularies[$id] = $vocabulary; - break; - } + if ($info['cardinality'] == 1) { + break; } + $i++; } - return $vocabularies[$id]; } /** - * Return the vocabulary identifier, the vocabulary's vid or module. + * Checks whether a term identified by name and vocabulary exists. Creates a + * new term if it does not exist. + * + * @param $name + * A term name. + * @param $vid + * A vocabulary id. * * @return - * Vocabulary's module name if it is a features vocabulary (= exportable), - * vocabulary's vid otherwise. + * A term id. */ -function taxonomy_vocabulary_id($vocabulary) { - if (strpos($vocabulary->module, 'features_') === 0) { - return $vocabulary->module; +function taxonomy_term_check_term($name, $vid) { + $terms = taxonomy_term_load_multiple(array(), array('name' => $name, 'vid' => $vid)); + if (empty($terms)) { + $term = new stdClass(); + $term->name = $name; + $term->vid = $vid; + taxonomy_term_save($term); + return $term->tid; } - return $vocabulary->vid; + $term = reset($terms); + return $term->tid; } diff --git a/plugins/FeedsFileFetcher.inc b/plugins/FeedsFileFetcher.inc index 05a07355..00bf398e 100644 --- a/plugins/FeedsFileFetcher.inc +++ b/plugins/FeedsFileFetcher.inc @@ -54,18 +54,11 @@ class FeedsFileFetcher extends FeedsFetcher { * Source form. */ public function sourceForm($source_config) { - $form = $info = array(); - if (!empty($source_config['source']) && file_exists($source_config['source'])) { - $wrapper = file_stream_wrapper_get_instance_by_uri($source_config['source']); - $path = $wrapper->getExternalUrl(); - $info = array( - 'path' => $path, - 'size' => filesize($source_config['source']), - ); - if (module_exists('mimedetect')) { - $info['mime'] = mimedetect_mime($source_config['source']); - } - } + $form = array(); + $form['fid'] = array( + '#type' => 'value', + '#value' => empty($source_config['fid']) ? 0 : $source_config['fid'], + ); $form['source'] = array( '#type' => empty($this->config['direct']) ? 'value' : 'textfield', '#title' => t('File'), @@ -77,7 +70,7 @@ class FeedsFileFetcher extends FeedsFetcher { '#title' => empty($this->config['direct']) ? t('File') : NULL, '#description' => empty($source_config['source']) ? t('Select the file to be imported from your local system.') : t('Select a different file to be imported from your local system.'), '#theme' => 'feeds_upload', - '#file_info' => $info, + '#file_info' => empty($source_config['fid']) ? NULL : file_load($source_config['fid']), '#size' => 10, ); return $form; @@ -92,8 +85,10 @@ class FeedsFileFetcher extends FeedsFetcher { // If there is a file uploaded, save it, otherwise validate input on // file. + // @todo: Track usage of file, remove file when removing source. if ($file = file_save_upload('feeds', array('file_validate_extensions' => array(0 => $this->config['allowed_extensions'])), $feed_dir)) { $values['source'] = $file->uri; + $values['file'] = $file; } elseif (empty($values['source'])) { form_set_error('feeds][source', t('Upload a file first.')); @@ -106,6 +101,39 @@ class FeedsFileFetcher extends FeedsFetcher { } } + /** + * Override parent::sourceSave(). + */ + public function sourceSave(FeedsSource $source) { + $source_config = $source->getConfigFor($this); + + // If a new file is present, delete the old one and replace it with the new + // one. + if (isset($source_config['file'])) { + $file = $source_config['file']; + if (isset($source_config['fid'])) { + $this->deleteFile($source_config['fid'], $source->feed_nid); + } + $file->status = FILE_STATUS_PERMANENT; + file_save($file); + file_usage_add($file, 'feeds', get_class($this), $source->feed_nid); + + $source_config['fid'] = $file->fid; + unset($source_config['file']); + $source->setConfigFor($this, $source_config); + } + } + + /** + * Override parent::sourceDelete(). + */ + public function sourceDelete(FeedsSource $source) { + $source_config = $source->getConfigFor($this); + if (isset($source_config['fid'])) { + $this->deleteFile($source_config['fid'], $source->feed_nid); + } + } + /** * Override parent::configDefaults(). */ @@ -135,4 +163,14 @@ class FeedsFileFetcher extends FeedsFetcher { ); return $form; } + + /** + * Helper. Deletes a file. + */ + protected function deleteFile($fid, $feed_nid) { + if ($file = file_load($fid)) { + file_usage_delete($file, 'feeds', get_class($this), $feed_nid); + file_delete($file); + } + } } diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc index 8e1dc478..6167d90d 100644 --- a/plugins/FeedsNodeProcessor.inc +++ b/plugins/FeedsNodeProcessor.inc @@ -96,7 +96,7 @@ class FeedsNodeProcessor extends FeedsProcessor { $nids = array(); $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE fn.id = :id AND fn.feed_nid = :nid", 0, $count, array(':id' => $source->id, ':nid' => $source->feed_nid)); foreach ($nodes as $node) { - $nids[] = $node->nid; + $nids[$node->nid] = $node->nid; $batch->deleted++; } node_delete_multiple($nids); @@ -295,7 +295,7 @@ class FeedsNodeProcessor extends FeedsProcessor { // Let other modules expose mapping targets. self::loadMappers(); - drupal_alter('feeds_node_processor_targets', $targets, $this->config['content_type']); + feeds_alter('feeds_processor_targets', $targets, 'node', $this->config['content_type']); return $targets; } @@ -358,11 +358,10 @@ class FeedsNodeProcessor extends FeedsProcessor { $node->feeds_node_item->guid = ''; } - static $included; - if (!$included) { - module_load_include('inc', 'node', 'node.pages'); - $included = TRUE; - } + // Give mappers a hint at what they're operating on. + $node->entity_type = 'node'; + + // Let other modules populate default values. node_object_prepare($node); // Populate properties that are set by node_object_prepare(). diff --git a/plugins/FeedsParser.inc b/plugins/FeedsParser.inc index 254f2897..4319a5fb 100644 --- a/plugins/FeedsParser.inc +++ b/plugins/FeedsParser.inc @@ -49,7 +49,7 @@ abstract class FeedsParser extends FeedsPlugin { public function getMappingSources() { self::loadMappers(); $sources = array(); - drupal_alter('feeds_parser_sources', $sources, feeds_importer($this->id)->config['content_type']); + feeds_alter('feeds_parser_sources', $sources, feeds_importer($this->id)->config['content_type']); if (!feeds_importer($this->id)->config['content_type']) { return $sources; } @@ -204,15 +204,6 @@ class FeedsEnclosure extends FeedsElement { $this->mime_type = $mime_type; } - /** - * Destructor, clean up any temporary files. - */ - public function __destruct() { - if (!empty($this->file)) { - file_delete($this->file); - } - } - /** * @return * MIME type of return value of getValue(). @@ -235,41 +226,51 @@ class FeedsEnclosure extends FeedsElement { } /** + * Get a Drupal file object of the enclosed resource, download if necessary. + * + * @param $destination + * The path or uri specifying the target directory in which the file is + * expected. If not given, a previously defined directory will be assumed. + * The default directory is tmp. + * * @return - * A temporary file path to the downloaded resource referenced by the - * enclosure. Downloads resource if not downloaded yet. The file path is - * valid for the time of the page load. + * A file path to the enclosed resource. * - * @todo Get file extension from mime_type. - * @todo This is not concurrency safe. + * @todo file_destination(*, FILE_EXISTS_RENAME) is not concurrency safe. */ - public function getFile() { - if(empty($this->file) && $this->getValue()) { - // Check if this enclosure contains a local file. - if (!parse_url($this->getValue(), PHP_URL_SCHEME)) { - if (strpos($this->getValue(), 'public://') === 0) { - if (file_exists($this->getValue())) { - $this->file = $this->getValue(); - return $this->file; - } - } - throw new Exception(t('Invalid enclosure %enclosure', array('%enclosure' => $this->getValue()))); - } - $filename = basename($this->getValue()); - if (module_exists('transliteration')) { - require_once (drupal_get_path('module', 'transliteration') . '/transliteration.inc'); - $filename = transliteration_clean_filename($filename); + public function getFile($destination) { + + if (empty($this->file) && $this->getValue()) { + + // File is in the right place, nothing to do. + if (strpos($this->getValue(), $directory) === 0) { + $this->file = $this->getValue(); + return $this->file; } - $dest = file_destination(file_directory_temp() .'/'. $filename, FILE_EXISTS_RENAME); - if (ini_get('allow_url_fopen')) { - $this->file = copy($this->getValue(), $dest) ? $dest : 0; + + // Prepare destination directory. + file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY); + + // Copy or save file depending its location. + if (drupal_realpath($this->getValue())) { + $file = new stdClass(); + $file->uid = $user->uid; + $file->status = FILE_STATUS_PERMANENT; + $file->uri = $this->getValue(); + $this->file = file_copy($file, $destination); } else { - $file = file_save_data($this->getContent(), $dest); - $this->file = $file ? $file->uri : 0; + $filename = basename($this->getValue()); + if (module_exists('transliteration')) { + require_once (drupal_get_path('module', 'transliteration') . '/transliteration.inc'); + $filename = transliteration_clean_filename($filename); + } + $this->file = file_save_data($this->getContent(), "$destination/$filename"); } - if ($this->file === 0) { - throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest))); + + // We couldn't make sense of this enclosure, throw an exception. + if (!$this->file) { + throw new Exception(t('Invalid enclosure %enclosure', array('%enclosure' => $this->getValue()))); } } return $this->file; diff --git a/plugins/FeedsPlugin.inc b/plugins/FeedsPlugin.inc index debc4eec..3e394fd1 100644 --- a/plugins/FeedsPlugin.inc +++ b/plugins/FeedsPlugin.inc @@ -83,6 +83,8 @@ abstract class FeedsPlugin extends FeedsConfigurable implements FeedsSourceInter * * @see FeedsNodeProcessor::map() * @see FeedsUserProcessor::map() + * + * @todo: Use CTools Plugin API. */ protected static function loadMappers() { static $loaded = FALSE; @@ -90,15 +92,107 @@ abstract class FeedsPlugin extends FeedsConfigurable implements FeedsSourceInter $path = drupal_get_path('module', 'feeds') .'/mappers'; $files = drupal_system_listing('/.*\.inc$/', $path, 'name', 0); foreach ($files as $file) { - if (strstr($file->filename, '/mappers/')) { - require_once("./$file->filename"); + if (strstr($file->uri, '/mappers/')) { + require_once("./$file->uri"); } } - // Rebuild cache. - module_implements('', FALSE, TRUE); } $loaded = TRUE; } + + /** + * Get all available plugins. + */ + public static function all() { + ctools_include('plugins'); + $plugins = ctools_get_plugins('feeds', 'plugins'); + + $result = array(); + foreach ($plugins as $key => $info) { + if (!empty($info['hidden'])) { + continue; + } + $result[$key] = $info; + } + + // Sort plugins by name and return. + uasort($result, 'feeds_plugin_compare'); + return $result; + } + + /** + * Determines whether given plugin is derived from given base plugin. + * + * @param $plugin_key + * String that identifies a Feeds plugin key. + * @param $parent_plugin + * String that identifies a Feeds plugin key to be tested against. + * + * @return + * TRUE if $parent_plugin is directly *or indirectly* a parent of $plugin, + * FALSE otherwise. + */ + public static function child($plugin_key, $parent_plugin) { + ctools_include('plugins'); + $plugins = ctools_get_plugins('feeds', 'plugins'); + $info = $plugins[$plugin_key]; + + if (empty($info['handler']['parent'])) { + return FALSE; + } + elseif ($info['handler']['parent'] == $parent_plugin) { + return TRUE; + } + else { + return self::child($info['handler']['parent'], $parent_plugin); + } + } + + /** + * Determines the type of a plugin. + * + * @todo PHP5.3: Implement self::type() and query with $plugin_key::type(). + * + * @param $plugin_key + * String that identifies a Feeds plugin key. + * + * @return + * One of the following values: + * 'fetcher' if the plugin is a fetcher + * 'parser' if the plugin is a parser + * 'processor' if the plugin is a processor + * FALSE otherwise. + */ + public static function typeOf($plugin_key) { + if (self::child($plugin_key, 'FeedsFetcher')) { + return 'fetcher'; + } + elseif (self::child($plugin_key, 'FeedsParser')) { + return 'parser'; + } + elseif (self::child($plugin_key, 'FeedsProcessor')) { + return 'processor'; + } + return FALSE; + } + + /** + * Gets all available plugins of a particular type. + * + * @param $type + * 'fetcher', 'parser' or 'processor' + */ + public static function byType($type) { + $plugins = self::all(); + + $result = array(); + foreach ($plugins as $key => $info) { + if ($type == self::typeOf($key)) { + $result[$key] = $info; + } + } + return $result; + } } /** @@ -109,3 +203,10 @@ class FeedsMissingPlugin extends FeedsPlugin { return array(); } } + +/** + * Sort callback for FeedsPlugin::all(). + */ +function feeds_plugin_compare($a, $b) { + return strcasecmp($a['name'], $b['name']); +} diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc index 4dd3f172..7ee9a482 100644 --- a/plugins/FeedsProcessor.inc +++ b/plugins/FeedsProcessor.inc @@ -101,10 +101,6 @@ abstract class FeedsProcessor extends FeedsPlugin { // Many mappers add to existing fields rather than replacing them. Hence we // need to clear target elements of each item before mapping in case we are // mapping on a prepopulated item such as an existing node. - if (is_array($target_item)) { - $target_item = (object)$target_item; - $convert_to_array = TRUE; - } foreach ($this->config['mappings'] as $mapping) { if (isset($targets[$mapping['target']]['real_target'])) { unset($target_item->{$targets[$mapping['target']]['real_target']}); @@ -113,9 +109,6 @@ abstract class FeedsProcessor extends FeedsPlugin { unset($target_item->{$mapping['target']}); } } - if ($convert_to_array) { - $target_item = (array)$target_item; - } /* This is where the actual mapping happens: For every mapping we envoke @@ -194,8 +187,8 @@ abstract class FeedsProcessor extends FeedsPlugin { * * @ingroup mappingapi */ - public function setTargetElement(&$target_item, $target_element, $value) { - $target_item[$target_element] = $value; + public function setTargetElement($target_item, $target_element, $value) { + $target_item->$target_element = $value; } /** diff --git a/plugins/FeedsSimplePieParser.inc b/plugins/FeedsSimplePieParser.inc index 18875688..548c46de 100644 --- a/plugins/FeedsSimplePieParser.inc +++ b/plugins/FeedsSimplePieParser.inc @@ -211,7 +211,7 @@ class FeedsSimplePieParser extends FeedsParser { * Returns cache directory. Creates it if it doesn't exist. */ protected function cacheDirectory() { - $directory = file_directory_path() .'/simplepie'; + $directory = 'public://simplepie'; file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); return $directory; } diff --git a/plugins/FeedsTermProcessor.inc b/plugins/FeedsTermProcessor.inc index 8bc9787e..92558a2a 100644 --- a/plugins/FeedsTermProcessor.inc +++ b/plugins/FeedsTermProcessor.inc @@ -28,28 +28,29 @@ class FeedsTermProcessor extends FeedsProcessor { if (!($tid = $this->existingItemId($batch, $source)) || $this->config['update_existing'] != FEEDS_SKIP_EXISTING) { // Map item to a term. - $term = array(); + $term = new stdClass(); if ($tid && $this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { - $term = (array) taxonomy_get_term($tid, TRUE); + $term = taxonomy_term_load($tid); $term = module_invoke_all('feeds_taxonomy_load', $term); } + $term->entity_type = 'taxonomy_term'; $term = $this->map($batch, $term, $source->feed_nid); // Check if term name is set, otherwise continue. - if (empty($term['name'])) { + if (empty($term->name)) { $no_name++; continue; } // Add term id if available. if (!empty($tid)) { - $term['tid'] = $tid; + $term->tid = $tid; } // Save the term. - $term['feeds_importer_id'] = $this->id; - $term['feed_nid'] = $this->feed_nid; - taxonomy_save_term($term); + $term->feeds_importer_id = $this->id; + $term->feed_nid = $source->feed_nid; + taxonomy_term_save($term); if ($tid) { $updated++; } @@ -90,7 +91,7 @@ class FeedsTermProcessor extends FeedsProcessor { $deleted = 0; $vocabulary = $this->vocabulary(); $terms = db_query("SELECT td.tid - FROM {term_data} td + FROM {taxonomy_term_data} td JOIN {feeds_term_item} ft ON td.tid = ft.tid WHERE td.vid = :vid AND ft.id = :id @@ -101,7 +102,7 @@ class FeedsTermProcessor extends FeedsProcessor { ':feed_nid' => $source->feed_nid, )); foreach ($terms as $term) { - if (taxonomy_del_term($term->tid) == SAVED_DELETED) { + if (taxonomy_term_delete($term->tid) == SAVED_DELETED) { $deleted++; } } @@ -120,17 +121,13 @@ class FeedsTermProcessor extends FeedsProcessor { protected function map(FeedsImportBatch $batch, $target_term = NULL) { // Prepare term object, have parent class do the iterating. if (!$target_term) { - $target_term = array(); + $target_term = new stdClass(); } if (!$vocabulary = $this->vocabulary()) { throw new Exception(t('No vocabulary specified for term processor')); } - $target_term['vid'] = $vocabulary->vid; + $target_term->vid = $vocabulary->vid; $target_term = parent::map($batch, $target_term); - // Taxonomy module expects synonyms to be supplied as a single string. - if (isset($target_term['synonyms']) && is_array(isset($target_term['synonyms']))) { - $target_term['synonyms'] = implode("\n", $target_term['synonyms']); - } return $target_term; } @@ -150,13 +147,8 @@ class FeedsTermProcessor extends FeedsProcessor { */ public function configForm(&$form_state) { $options = array(0 => t('Select a vocabulary')); - foreach (taxonomy_get_vocabularies() as $vid => $vocab) { - if (strpos($vocab->module, 'features_') === 0) { - $options[$vocab->module] = $vocab->name; - } - else { - $options[$vid] = $vocab->name; - } + foreach (taxonomy_get_vocabularies() as $vocab) { + $options[$vocab->machine_name] = check_plain($vocab->name); } $form = array(); $form['vocabulary'] = array( @@ -203,14 +195,12 @@ class FeedsTermProcessor extends FeedsProcessor { 'name' => t('Term description'), 'description' => t('Description of the taxonomy term.'), ), - 'synonyms' => array( - 'name' => t('Term synonyms'), - 'description' => t('One synonym or an array of synonyms of the taxonomy term.'), - ), ); // Let implementers of hook_feeds_term_processor_targets() add their targets. - $vocabulary = $this->vocabulary(); - drupal_alter('feeds_term_processor_targets', $targets, $vocabulary->vid); + if ($vocabulary = $this->vocabulary()) { + self::loadMappers(); + feeds_alter('feeds_processor_targets', $targets, 'taxonomy_term', $vocabulary->machine_name); + } return $targets; } @@ -223,7 +213,7 @@ class FeedsTermProcessor extends FeedsProcessor { foreach ($this->uniqueTargets($batch) as $target => $value) { if ($target == 'name') { $vocabulary = $this->vocabulary(); - if ($tid = db_query("SELECT tid FROM {term_data} WHERE name = :name AND vid = :vid", array(':name' => $value, ':vid' => $vocabulary->vid))->fetchField()) { + if ($tid = db_query("SELECT tid FROM {taxonomy_term_data} WHERE name = :name AND vid = :vid", array(':name' => $value, ':vid' => $vocabulary->vid))->fetchField()) { return $tid; } } @@ -233,20 +223,23 @@ class FeedsTermProcessor extends FeedsProcessor { /** * Return vocabulary to map to. - * - * Feeds supports looking up vocabularies by their module name as part of an - * effort to use the vocabulary.module field as machine name to make - * vocabularies exportable. */ public function vocabulary() { - $vocabularies = taxonomy_get_vocabularies(); + // Legacy handling for old feeds importers. if (is_numeric($this->config['vocabulary'])) { - return $vocabularies[$this->config['vocabulary']]; + $vocabularies = taxonomy_get_vocabularies(); + return isset($vocabularies[$this->config['vocabulary']]) ? $vocabularies[$this->config['vocabulary']] : NULL; } else { - foreach ($vocabularies as $vocabulary) { - if ($vocabulary->module == $this->config['vocabulary']) { - return $vocabulary; + if ($vocabulary = taxonomy_vocabulary_machine_name_load($this->config['vocabulary'])) { + return $vocabulary; + } + else { + $vocabularies = taxonomy_get_vocabularies(); + foreach ($vocabularies as $vocabulary) { + if ($vocabulary->module == $this->config['vocabulary']) { + return $vocabulary; + } } } } diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc index 1f187b6f..ab9ffe54 100644 --- a/plugins/FeedsUserProcessor.inc +++ b/plugins/FeedsUserProcessor.inc @@ -98,6 +98,7 @@ class FeedsUserProcessor extends FeedsProcessor { // Prepare term object. $target_account = new stdClass(); $target_account->uid = 0; + $target_account->entity_type = 'user'; $target_account->roles = array_filter($this->config['roles']); $target_account->status = $this->config['status']; @@ -189,7 +190,7 @@ class FeedsUserProcessor extends FeedsProcessor { // Let other modules expose mapping targets. self::loadMappers(); - drupal_alter('feeds_user_processor_targets', $targets); + feeds_alter('feeds_processor_targets', $targets, 'user'); return $targets; } diff --git a/tests/feeds.test.inc b/tests/feeds.test.inc index 9b9a2354..d057befa 100644 --- a/tests/feeds.test.inc +++ b/tests/feeds.test.inc @@ -10,14 +10,10 @@ * Test basic Data API functionality. */ class FeedsWebTestCase extends DrupalWebTestCase { - - public function __construct($test_id = NULL) { - parent::__construct($test_id); - // Force the feeds test profile. This unfortunately an either-or decision: - // either Feeds tests run in feeds_test profile or they run in another - // profile. Both is not possible. See http://drupal.org/node/911354 - $this->profile = 'feeds_test'; - } + // Force the feeds test profile. This unfortunately an either-or decision: + // either Feeds tests run in feeds_test profile or they run in another + // profile. Both is not possible. See http://drupal.org/node/911354 + protected $profile = 'feeds_test'; /** * Debug utility. Shows current screen. @@ -118,7 +114,7 @@ class FeedsWebTestCase extends DrupalWebTestCase { * feeds_feeds_plugins()). */ public function setPlugin($id, $plugin_key) { - if ($type = feeds_plugin_type($plugin_key)) { + if ($type = FeedsPlugin::typeOf($plugin_key)) { $edit = array( 'plugin_key' => $plugin_key, ); @@ -161,9 +157,14 @@ class FeedsWebTestCase extends DrupalWebTestCase { if (empty($feed_url)) { $feed_url = $GLOBALS['base_url'] .'/'. drupal_get_path('module', 'feeds') .'/tests/feeds/developmentseed.rss2'; } + // If content type not given, retrieve it. if (!$content_type) { - $config = unserialize(db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => $id))->fetchField()); + $result= db_select('feeds_importer', 'f') + ->condition('f.id', $id, '=') + ->fields('f', array('config')) + ->execute(); + $config = unserialize($result->fetchField()); $content_type = $config['content_type']; $this->assertFalse(empty($content_type), 'Valid content type found: '. $content_type); } @@ -180,8 +181,18 @@ class FeedsWebTestCase extends DrupalWebTestCase { $nid = $this->getNid($this->getUrl()); // Check whether feed got recorded in feeds_source table. - $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {feeds_source} WHERE id = :id AND feed_nid = :feed_nid", array(':id'=> $id, ':feed_nid' => $nid))->fetchField()); - $source = db_query("SELECT * FROM {feeds_source} WHERE id = :id AND feed_nid = :feed_nid", array(':id'=> $id, ':feed_nid' => $nid))->fetch(); + $query = db_select('feeds_source', 's') + ->condition('s.id', $id, '=') + ->condition('s.feed_nid', $nid, '='); + $query->addExpression("COUNT(*)"); + $result = $query->execute()->fetchField(); + $this->assertEqual(1, $result); + + $source = db_select('feeds_source', 's') + ->condition('s.id', $id, '=') + ->condition('s.feed_nid', $nid, '=') + ->fields('s', array('config')) + ->execute()->fetch(); $config = unserialize($source->config); $this->assertEqual($config['FeedsHTTPFetcher']['source'], $feed_url, t('URL in DB correct.')); return $nid; @@ -207,7 +218,11 @@ class FeedsWebTestCase extends DrupalWebTestCase { $this->assertText('has been updated.'); // Check that the URL was updated in the feeds_source table. - $source = db_query("SELECT * FROM {feeds_source} WHERE feed_nid = :nid", array(':nid' => $nid))->fetch(); + //$source = db_query("SELECT * FROM {feeds_source} WHERE feed_nid = :nid", array(':nid' => $nid))->fetch(); + $source = db_select('feeds_source', 's') + ->condition('s.feed_nid', $nid, '=') + ->fields('s', array('config')) + ->execute()->fetch(); $config = unserialize($source->config); $this->assertEqual($config['FeedsHTTPFetcher']['source'], $feed_url, t('URL in DB correct.')); } diff --git a/tests/feeds_processor_node.test b/tests/feeds_processor_node.test index 188a4e8f..823269f3 100644 --- a/tests/feeds_processor_node.test +++ b/tests/feeds_processor_node.test @@ -30,25 +30,25 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { */ public function setUp() { parent::setUp('feeds', 'feeds_ui', 'ctools', 'job_scheduler'); - $this->drupalLogin( - $this->drupalCreateUser( - array( - 'administer feeds', 'administer nodes', 'administer content types', - ) - ) - ); + $user = $this->drupalCreateUser(array( + 'administer site configuration', + 'administer feeds', + 'administer nodes', + 'administer content types', + 'create page content', + 'edit any page content', + )); + $this->drupalLogin($user); } /** * Test node creation, refreshing/deleting feeds and feed items. */ public function test() { - // Set the teaser length to unlimited otherwise tests looking for text on - // nodes will fail. - $edit = array( - 'teaser_length' => 0, - ); - $this->drupalPost('admin/content/node-settings', $edit, 'Save configuration'); + // Set the teaser length display to unlimited otherwise tests looking for + // text on nodes will fail. + $edit['fields[body][type]'] = 'text_default'; + $this->drupalPost('admin/structure/types/manage/article/display/teaser', $edit, 'Save'); // Create an importer configuration. $this->createImporterConfiguration('Syndication', 'syndication'); @@ -83,6 +83,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { ); $nid = $this->createFeedNode(); + // Assert 10 items aggregated after creation of the node. $this->assertText('Created 10 Article nodes.'); @@ -96,49 +97,23 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->drupalGet('node/'. $article_nid); $this->assertNoRaw('node/'. $article_nid .'/import'); $this->assertNoRaw('node/'. $article_nid .'/delete-items'); - $this->assertEqual("Created/updated by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revisions} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField()); + $this->assertEqual("Created/updated by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revision} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField()); // Assert accuracy of aggregated information. $this->drupalGet('node'); - $this->assertPattern('/<span class="submitted">(.*?)Anonymous<\/span>/'); - $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates'); - $this->assertText('Tue, 10/06/2009'); - $this->assertText('A new translation process for Open Atrium & integration with Localize Drupal'); - $this->assertText('Week in DC Tech: October 5th Edition'); - $this->assertText('Mon, 10/05/2009'); - $this->assertText('There are some great technology events happening this week'); - $this->assertText('Mapping Innovation at the World Bank with Open Atrium'); - $this->assertText('Fri, 10/02/2009'); - $this->assertText('Open Atrium is being used as a base platform for collaboration'); - $this->assertText('September GeoDC Meetup Tonight'); - $this->assertText('Wed, 09/30/2009'); - $this->assertText('Today is the last Wednesday of the month'); - $this->assertText('Week in DC Tech: September 28th Edition'); - $this->assertText('Mon, 09/28/2009'); - $this->assertText('Looking to geek out this week? There are a bunch of'); - $this->assertText('Open Data for Microfinance: The New MIXMarket.org'); - $this->assertText('Thu, 09/24/2009'); - $this->assertText('There are profiles for every country that the MIX Market is hosting.'); - $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet'); - $this->assertText('Tue, 09/22/2009'); - $this->assertText('In addition to authentication, the Siteminder system'); - $this->assertText('Week in DC Tech: September 21 Edition'); - $this->assertText('Mon, 09/21/2009'); - $this->assertText('an interesting variety of technology events happening in Washington, DC '); - $this->assertText('s Software Freedom Day: Impressions & Photos'); - $this->assertText('Mon, 09/21/2009'); - $this->assertText('Presenting on Features in Drupal and Open Atrium'); - $this->assertText('Scaling the Open Atrium UI'); - $this->assertText('Fri, 09/18/2009'); - $this->assertText('The first major change is switching'); - + $this->assertRaw('<span class="username">Anonymous (not verified)</span>'); + $this->assertDevseedFeedContent(); + // Assert DB status. $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid")->fetchField(); $this->assertEqual($count, 10, 'Accurate number of items in database.'); // Assert default input format on first imported feed node. - $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revisions} nr ON n.vid = nr.vid", 0, 1)->fetchField(); +/* +// NEEDS update. + $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField(); $this->assertEqual($format, filter_fallback_format(), 'Using default Input format.'); +*/ // Import again. $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); @@ -155,7 +130,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $edit = array( 'node_options[status]' => FALSE, ); - $this->drupalPost('admin/content/node-type/article', $edit, t('Save content type')); + $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type')); $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE n.status = 0")->fetchField(); @@ -163,7 +138,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $edit = array( 'node_options[status]' => TRUE, ); - $this->drupalPost('admin/content/node-type/article', $edit, t('Save content type')); + $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type')); $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); // Enable replace existing and import updated feed file. @@ -209,20 +184,23 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { // Assert author. $this->drupalGet('node'); - $this->assertPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/'); + $this->assertPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/'); $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $author->uid))->fetchField(); $this->assertEqual($count, 10, 'Accurate number of items in database.'); // Assert input format. - $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revisions} nr ON n.vid = nr.vid", 0, 1)->fetchField(); +/* +// NEEDS update. + $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField(); $this->assertEqual($format, filter_fallback_format() + 1, 'Set non-default Input format.'); +*/ // Set to update existing, remove authorship of above nodes and import again. $this->setSettings('syndication', 'FeedsNodeProcessor', array('update_existing' => 2)); db_query("UPDATE {node} n JOIN {feeds_node_item} fi ON n.nid = fi.nid SET n.uid = 0, fi.hash=''"); $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); $this->drupalGet('node'); - $this->assertNoPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/'); + $this->assertNoPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/'); $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $author->uid))->fetchField(); $this->assertEqual($count, 0, 'Accurate number of items in database.'); @@ -238,7 +216,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { ); $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); $this->drupalGet('node'); - $this->assertNoPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/'); + $this->assertNoPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/'); $uid = db_query("SELECT uid FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetchField(); $count = db_query("SELECT COUNT(*) FROM {node} WHERE uid = :uid", array(':uid' =>$uid))->fetchField(); $this->assertEqual($count, 11, 'All feed item nodes are assigned to feed node author.'); @@ -304,36 +282,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { // Assert accuracy of aggregated information. $this->drupalGet('node'); - $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates'); - $this->assertText('Tue, 10/06/2009'); - $this->assertText('A new translation process for Open Atrium & integration with Localize Drupal'); - $this->assertText('Week in DC Tech: October 5th Edition'); - $this->assertText('Mon, 10/05/2009'); - $this->assertText('There are some great technology events happening this week'); - $this->assertText('Mapping Innovation at the World Bank with Open Atrium'); - $this->assertText('Fri, 10/02/2009'); - $this->assertText('Open Atrium is being used as a base platform for collaboration'); - $this->assertText('September GeoDC Meetup Tonight'); - $this->assertText('Wed, 09/30/2009'); - $this->assertText('Today is the last Wednesday of the month'); - $this->assertText('Week in DC Tech: September 28th Edition'); - $this->assertText('Mon, 09/28/2009'); - $this->assertText('Looking to geek out this week? There are a bunch of'); - $this->assertText('Open Data for Microfinance: The New MIXMarket.org'); - $this->assertText('Thu, 09/24/2009'); - $this->assertText('There are profiles for every country that the MIX Market is hosting.'); - $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet'); - $this->assertText('Tue, 09/22/2009'); - $this->assertText('In addition to authentication, the Siteminder system'); - $this->assertText('Week in DC Tech: September 21 Edition'); - $this->assertText('Mon, 09/21/2009'); - $this->assertText('an interesting variety of technology events happening in Washington, DC '); - $this->assertText('s Software Freedom Day: Impressions & Photos'); - $this->assertText('Mon, 09/21/2009'); - $this->assertText('Presenting on Features in Drupal and Open Atrium'); - $this->assertText('Scaling the Open Atrium UI'); - $this->assertText('Fri, 09/18/2009'); - $this->assertText('The first major change is switching'); + $this->assertDevseedFeedContent(); // Assert DB status. $count = db_query("SELECT COUNT(*) FROM {feeds_node_item}")->fetchField(); @@ -384,17 +333,19 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->assertEqual($count, 10, 'Accurate number of items in database.'); // Login with new user with only access content permissions. - $this->drupalLogin( - $this->drupalCreateUser() - ); + $this->drupalLogin($this->drupalCreateUser()); + // Navigate to feed import form, access should be denied. $this->drupalGet('import/syndication_standalone'); $this->assertResponse(403); // Use File Fetcher. - $this->drupalLogin( - $this->drupalCreateUser(array('administer feeds', 'administer nodes')) - ); + $this->drupalLogin($this->drupalCreateUser(array( + 'administer feeds', + 'administer nodes', + 'create page content', + ))); + $this->setPlugin('syndication', 'FeedsFileFetcher'); // Create a feed node. $edit = array( @@ -404,4 +355,40 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->assertText('has been created.'); $this->assertText('Created 25 Article nodes.'); } + + /** + * Check thet contents of the current page for the DS feed. + */ + function assertDevseedFeedContent() { + $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates'); + $this->assertText('Tue, 10/06/2009'); + $this->assertText('A new translation process for Open Atrium & integration with Localize Drupal'); + $this->assertText('Week in DC Tech: October 5th Edition'); + $this->assertText('Mon, 10/05/2009'); + $this->assertText('There are some great technology events happening this week'); + $this->assertText('Mapping Innovation at the World Bank with Open Atrium'); + $this->assertText('Fri, 10/02/2009'); + $this->assertText('is being used as a base platform for collaboration at the World Bank'); + $this->assertText('September GeoDC Meetup Tonight'); + $this->assertText('Wed, 09/30/2009'); + $this->assertText('Today is the last Wednesday of the month'); + $this->assertText('Week in DC Tech: September 28th Edition'); + $this->assertText('Mon, 09/28/2009'); + $this->assertText('Looking to geek out this week? There are a bunch of'); + $this->assertText('Open Data for Microfinance: The New MIXMarket.org'); + $this->assertText('Thu, 09/24/2009'); + $this->assertText('There are profiles for every country that the MIX Market is hosting.'); + $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet'); + $this->assertText('Tue, 09/22/2009'); + $this->assertText('In addition to authentication, the Siteminder system'); + $this->assertText('Week in DC Tech: September 21 Edition'); + $this->assertText('Mon, 09/21/2009'); + $this->assertText('an interesting variety of technology events happening in Washington, DC '); + $this->assertText('s Software Freedom Day: Impressions & Photos'); + $this->assertText('Mon, 09/21/2009'); + $this->assertText('Presenting on Features in Drupal and Open Atrium'); + $this->assertText('Scaling the Open Atrium UI'); + $this->assertText('Fri, 09/18/2009'); + $this->assertText('The first major change is switching'); + } } diff --git a/tests/feeds_processor_term.test b/tests/feeds_processor_term.test index ef43322c..8efdabea 100644 --- a/tests/feeds_processor_term.test +++ b/tests/feeds_processor_term.test @@ -29,7 +29,7 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase { * Set up test. */ public function setUp() { - parent::setUp('feeds', 'feeds_ui', 'ctools', 'job_scheduler'); + parent::setUp('taxonomy', 'ctools', 'feeds', 'feeds_ui', 'job_scheduler'); $this->drupalLogin( $this->drupalCreateUser( @@ -69,18 +69,19 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase { $edit = array( 'name' => 'Addams vocabulary', + 'machine_name' => 'addams', ); - $this->drupalPost('admin/content/taxonomy/add/vocabulary', $edit, t('Save')); + $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save')); $edit = array( - 'vocabulary' => 1, + 'vocabulary' => 'addams', ); $this->drupalPost('admin/structure/feeds/edit/term_import/settings/FeedsTermProcessor', $edit, t('Save')); // Import and assert. $this->importFile('term_import', $this->absolutePath() .'/tests/feeds/users.csv'); $this->assertText('Created 5 terms in Addams vocabulary.'); - $this->drupalGet('admin/content/taxonomy/1'); + $this->drupalGet('admin/structure/taxonomy/addams'); $this->assertText('Morticia'); $this->assertText('Fester'); $this->assertText('Gomez'); @@ -94,9 +95,9 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase { $edit = array( 'name' => 'Cousin Itt', ); - $this->drupalPost('admin/content/taxonomy/1/add/term', $edit, t('Save')); + $this->drupalPost('admin/structure/taxonomy/addams/add', $edit, t('Save')); $this->drupalPost('import/term_import/delete-items', array(), t('Delete')); - $this->drupalGet('admin/content/taxonomy/1'); + $this->drupalGet('admin/structure/taxonomy/addams'); $this->assertText('Cousin Itt'); $this->assertNoText('Morticia'); $this->assertNoText('Fester'); -- GitLab