diff --git a/mappers/file.inc b/mappers/file.inc index 646d0556cac0f6fdfdf067f0a7961d2cbd9daf75..aa967b9df29176abc023a3aa624d5cce8970e717 100644 --- a/mappers/file.inc +++ b/mappers/file.inc @@ -4,27 +4,39 @@ * @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(). + * @see FeedsNodeProcessor::getMappingTargets() */ function file_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) { $info = field_info_field($name); if (in_array($info['type'], array('file', 'image'))) { - $targets[$name] = array( - 'name' => check_plain($instance['label']), + $targets[$name . ':uri'] = array( + 'name' => t('@label: URI', array('@label' => $instance['label'])), 'callback' => 'file_feeds_set_target', - 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), + 'description' => t('The URI of the @label field.', array('@label' => $instance['label'])), + 'real_target' => $name, ); + + if ($info['type'] == 'image') { + $targets[$name . ':alt'] = array( + 'name' => t('@label: Alt', array('@label' => $instance['label'])), + 'callback' => 'file_feeds_set_target', + 'description' => t('The alt tag of the @label field.', array('@label' => $instance['label'])), + 'real_target' => $name, + ); + $targets[$name . ':title'] = array( + 'name' => t('@label: Title', array('@label' => $instance['label'])), + 'callback' => 'file_feeds_set_target', + 'description' => t('The title of the @label field.', array('@label' => $instance['label'])), + 'real_target' => $name, + ); + } } } } @@ -40,56 +52,82 @@ function file_feeds_set_target($source, $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, file_get_mimetype($v)); - } - else { - unset($value[$k]); + + // Add default of uri for backwards compatibility. + list($field_name, $sub_field) = explode(':', $target . ':uri'); + $info = field_info_field($field_name); + + if ($sub_field == 'uri') { + + foreach ($value as $k => $v) { + if (!($v instanceof FeedsEnclosure)) { + if (is_string($v)) { + $value[$k] = new FeedsEnclosure($v, file_get_mimetype($v)); + } + else { + unset($value[$k]); + } } } - } - if (empty($value)) { - return; - } + if (empty($value)) { + return; + } + + static $destination; + if (!$destination) { + $entity_type = $source->importer->processor->entityType(); + $bundle = $source->importer->processor->bundle(); - $entity_type = $source->importer->processor->entityType(); + $instance_info = field_info_instance($entity_type, $field_name, $bundle); - // Determine file destination. - // @todo This needs review and debugging. - $instance_info = field_info_instance($entity_type, $target, $source->importer->processor->bundle()); - $info = field_info_field($target); - $data = array(); - if (!empty($entity->uid)) { - $data[$entity_type] = $entity; + // Determine file destination. + // @todo This needs review and debugging. + $data = array(); + if (!empty($entity->uid)) { + $data[$entity_type] = $entity; + } + $destination = file_field_widget_uri($info, $instance_info, $data); + } } - $destination = file_field_widget_uri($info, $instance_info, $data); // Populate entity. - $i = 0; - $field = isset($entity->$target) ? $entity->$target : array(); + $field = isset($entity->$field_name) ? $entity->$field_name : array(LANGUAGE_NONE => array()); + $delta = 0; foreach ($value as $v) { - try { - $file = $v->getFile($destination); + if ($info['cardinality'] == $delta) { + break; } - catch (Exception $e) { - watchdog_exception('Feeds', $e, nl2br(check_plain($e))); + + if (!isset($field[LANGUAGE_NONE][$delta])) { + $field[LANGUAGE_NONE][$delta] = array(); } - if ($file) { - $field['und'][$i] = (array)$file; - $field['und'][$i]['display'] = 1; // @todo: Figure out how to properly populate this field. - if ($info['cardinality'] == 1) { + + switch ($sub_field) { + case 'alt': + case 'title': + $field[LANGUAGE_NONE][$delta][$sub_field] = $v; + break; + + case 'uri': + try { + $file = $v->getFile($destination); + $field[LANGUAGE_NONE][$delta] += (array) $file; + // @todo: Figure out how to properly populate this field. + $field[LANGUAGE_NONE][$delta]['display'] = 1; + } + catch (Exception $e) { + watchdog_exception('Feeds', $e, nl2br(check_plain($e))); + } break; - } - $i++; } + + $delta++; } - $entity->{$target} = $field; + + $entity->$field_name = $field; } diff --git a/tests/feeds.test b/tests/feeds.test index 480a5db7f9e6fb5941b6258b9721db4285b4c2ed..d54d56cfe21a2ba559d6351acd4c4c1a9735243e 100644 --- a/tests/feeds.test +++ b/tests/feeds.test @@ -18,11 +18,11 @@ class FeedsWebTestCase extends DrupalWebTestCase { // array of module names to setUp(). if (isset($args[0])) { if (is_array($args[0])) { - $modules = $args[0]; - } + $modules = $args[0]; + } else { - $modules = $args; - } + $modules = $args; + } } else { $modules = array(); diff --git a/tests/feeds/feeds-tests-files.tpl.php b/tests/feeds/feeds-tests-files.tpl.php index 9674abb2d46b3fb06cc4fe25cbc8220752937f71..a4ca2f9249ed766867f8114b4a83f99b1966993d 100644 --- a/tests/feeds/feeds-tests-files.tpl.php +++ b/tests/feeds/feeds-tests-files.tpl.php @@ -1,6 +1,6 @@ -Title,published,file,GUID -"Tubing is awesome",205200720,<?php print $files[0]; ?>,0 -"Jeff vs Tom",428112720,<?php print $files[1]; ?>,1 -"Attersee",1151766000,<?php print $files[2]; ?>,2 -"H Street NE",1256326995,<?php print $files[3]; ?>,3 -"La Fayette Park",1256326995,<?php print $files[4]; ?>,4 +Title,published,file,GUID,alt,title2 +"Tubing is awesome",205200720,<?php print $files[0]; ?>,0,Alt text 0,Title text 0 +"Jeff vs Tom",428112720,<?php print $files[1]; ?>,1,Alt text 1,Title text 1 +"Attersee",1151766000,<?php print $files[2]; ?>,2,Alt text 2,Title text 2 +"H Street NE",1256326995,<?php print $files[3]; ?>,3,Alt text 3,Title text 3 +"La Fayette Park",1256326995,<?php print $files[4]; ?>,4,Alt text 4,Title text 4 diff --git a/tests/feeds_mapper.test b/tests/feeds_mapper.test index a570dfae6cea34fd02cf6c5640b51079e0595e0f..81665b63318294baf35619eae2348b889ba8b0ab 100644 --- a/tests/feeds_mapper.test +++ b/tests/feeds_mapper.test @@ -19,8 +19,8 @@ class FeedsMapperTestCase extends FeedsWebTestCase { 'email' => 'email_textfield', 'emimage' => 'emimage_textfields', 'emaudio' => 'emaudio_textfields', - 'filefield' => 'filefield_widget', - 'image' => 'imagefield_widget', + 'file' => 'file_generic', + 'image' => 'image_image', 'link_field' => 'link_field', 'number_float' => 'number', 'number_integer' => 'number', diff --git a/tests/feeds_mapper_file.test b/tests/feeds_mapper_file.test index 20c79122150dc2183f1692db913f1ce3aaf520de..dcf5d98149402a1d98ab8939f4bf1b33f6e32aad 100644 --- a/tests/feeds_mapper_file.test +++ b/tests/feeds_mapper_file.test @@ -7,10 +7,6 @@ /** * Class for testing Feeds file mapper. - * - * @todo Add a test for enclosures using a local file that is - * a) in place and that - * b) needs to be copied from one location to another. */ class FeedsMapperFileTestCase extends FeedsMapperTestCase { public static function getInfo() { @@ -25,12 +21,12 @@ class FeedsMapperFileTestCase extends FeedsMapperTestCase { * Basic test loading a single entry CSV file. */ public function test() { - // If this is unset (or FALSE) http_request.inc will use curl, and will generate a 404 - // for this feel url provided by feeds_tests. However, if feeds_tests was enabled in your - // site before running the test, it will work fine. Since it is truly screwy, lets just - // force it to use drupal_http_request for this test case. + // If this is unset (or FALSE) http_request.inc will use curl, and will + // generate a 404 for this feel url provided by feeds_tests. However, if + // feeds_tests was enabled in your site before running the test, it will + // work fine. Since it is truly screwy, lets just force it to use + // drupal_http_request for this test case. variable_set('feeds_never_use_curl', TRUE); - variable_set('clean_url', TRUE); // Only download simplepie if the plugin doesn't already exist somewhere. // People running tests locally might have it. @@ -51,21 +47,21 @@ class FeedsMapperFileTestCase extends FeedsMapperTestCase { $this->addMappings('syndication', array( 0 => array( 'source' => 'title', - 'target' => 'title' + 'target' => 'title', ), 1 => array( 'source' => 'timestamp', - 'target' => 'created' + 'target' => 'created', ), 2 => array( 'source' => 'enclosures', - 'target' => 'field_files' + 'target' => 'field_files:uri', ), )); $nid = $this->createFeedNode('syndication', $GLOBALS['base_url'] . '/testing/feeds/flickr.xml'); $this->assertText('Created 5 nodes'); - $files = $this->_testFiles(); + $files = $this->listTestFiles(); $entities = db_select('feeds_item') ->fields('feeds_item', array('entity_id')) ->condition('id', 'syndication') @@ -95,27 +91,28 @@ class FeedsMapperFileTestCase extends FeedsMapperTestCase { $this->addMappings('node', array( 0 => array( 'source' => 'title', - 'target' => 'title' + 'target' => 'title', ), 1 => array( 'source' => 'file', - 'target' => 'field_files' + 'target' => 'field_files:uri', ), )); // Import. $edit = array( - 'feeds[FeedsHTTPFetcher][source]' => $GLOBALS['base_url'] . '/testing/feeds/files.csv', + 'feeds[FeedsHTTPFetcher][source]' => url('testing/feeds/files.csv', array('absolute' => TRUE)), ); $this->drupalPost('import/node', $edit, 'Import'); $this->assertText('Created 5 nodes'); // Assert: files should be in resources/. - $files = $this->_testFiles(); + $files = $this->listTestFiles(); $entities = db_select('feeds_item') ->fields('feeds_item', array('entity_id')) ->condition('id', 'node') ->execute(); + foreach ($entities as $entity) { $this->drupalGet('node/' . $entity->entity_id . '/edit'); $f = new FeedsEnclosure(array_shift($files), NULL); @@ -137,11 +134,12 @@ class FeedsMapperFileTestCase extends FeedsMapperTestCase { $this->assertText('Created 5 nodes'); // Assert: files should be in images/ now. - $files = $this->_testFiles(); + $files = $this->listTestFiles(); $entities = db_select('feeds_item') ->fields('feeds_item', array('entity_id')) ->condition('id', 'node') ->execute(); + foreach ($entities as $entity) { $this->drupalGet('node/' . $entity->entity_id . '/edit'); $f = new FeedsEnclosure(array_shift($files), NULL); @@ -149,29 +147,85 @@ class FeedsMapperFileTestCase extends FeedsMapperTestCase { } // Deleting all imported items will delete the files from the images/ dir. - // @todo: for some reason the first file does not get deleted. -// $this->drupalPost('import/node/delete-items', array(), 'Delete'); -// foreach ($this->_testFiles() as $file) { -// $this->assertFalse(is_file("public://images/$file")); -// } + $this->drupalPost('import/node/delete-items', array(), 'Delete'); + foreach ($this->listTestFiles() as $file) { + $this->assertFalse(is_file("public://images/$file")); + } } /** - * Lists test files. + * Tests mapping to an image field. */ - public function _testFiles() { - return array('tubing.jpeg', 'foosball.jpeg', 'attersee.jpeg', 'hstreet.jpeg', 'la fayette.jpeg'); + public function testImages() { + variable_set('feeds_never_use_curl', TRUE); + + $typename = $this->createContentType(array(), array('images' => 'image')); + + // Enable title and alt mapping. + $edit = array( + 'instance[settings][alt_field]' => 1, + 'instance[settings][title_field]' => 1, + ); + $this->drupalPost("admin/structure/types/manage/$typename/fields/field_images", $edit, t('Save settings')); + + // Create a CSV importer configuration. + $this->createImporterConfiguration('Node import from CSV', 'image_test'); + $this->setPlugin('image_test', 'FeedsCSVParser'); + $this->setSettings('image_test', 'FeedsNodeProcessor', array('bundle' => $typename)); + $this->setSettings('image_test', NULL, array('content_type' => '')); + $this->addMappings('image_test', array( + 0 => array( + 'source' => 'title', + 'target' => 'title', + ), + 1 => array( + 'source' => 'file', + 'target' => 'field_images:uri', + ), + 2 => array( + 'source' => 'title2', + 'target' => 'field_images:title', + ), + 3 => array( + 'source' => 'alt', + 'target' => 'field_images:alt', + ), + )); + + // Import. + $edit = array( + 'feeds[FeedsHTTPFetcher][source]' => url('testing/feeds/files-remote.csv', array('absolute' => TRUE)), + ); + $this->drupalPost('import/image_test', $edit, 'Import'); + $this->assertText('Created 5 nodes'); + + // Assert files exist. + $files = $this->listTestFiles(); + $entities = db_select('feeds_item') + ->fields('feeds_item', array('entity_id')) + ->condition('id', 'image_test') + ->execute(); + + foreach ($entities as $i => $entity) { + $this->drupalGet('node/' . $entity->entity_id . '/edit'); + $f = new FeedsEnclosure(array_shift($files), NULL); + $this->assertRaw($f->getUrlEncodedValue()); + $this->assertRaw("Alt text $i"); + $this->assertRaw("Title text $i"); + } } /** - * Handle file field widgets. + * Lists test files. */ - public function selectFieldWidget($fied_name, $field_type) { - if ($field_type == 'file') { - return 'file_generic'; - } - else { - return parent::selectFieldWidget($fied_name, $field_type); - } + protected function listTestFiles() { + return array( + 'tubing.jpeg', + 'foosball.jpeg', + 'attersee.jpeg', + 'hstreet.jpeg', + 'la fayette.jpeg', + ); } + } diff --git a/tests/feeds_tests.module b/tests/feeds_tests.module index ade38b2ea396087e9bd8ac9e43cf81d38e6be37e..399708ba0a02802788942c99ccd413e050445793 100644 --- a/tests/feeds_tests.module +++ b/tests/feeds_tests.module @@ -19,6 +19,11 @@ function feeds_tests_menu() { 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); + $items['testing/feeds/files-remote.csv'] = array( + 'page callback' => 'feeds_tests_files_remote', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); return $items; } @@ -53,7 +58,7 @@ function feeds_tests_flickr() { ); $path = drupal_get_path('module', 'feeds_tests') . '/feeds/assets'; foreach ($images as &$image) { - $image = url("$path/$image", array('absolute' => TRUE)); + $image = file_create_url("$path/$image"); } drupal_add_http_header('Content-Type', 'application/rss+xml; charset=utf-8'); print theme('feeds_tests_flickr', array('image_urls' => $images)); @@ -78,7 +83,26 @@ function feeds_tests_files() { } /** - * Implements hook_feeds_processor_targets_alter() + * Outputs a CSV file pointing to files to download. + */ +function feeds_tests_files_remote() { + $images = array( + 0 => 'tubing.jpeg', + 1 => 'foosball.jpeg', + 2 => 'attersee.jpeg', + 3 => 'hstreet.jpeg', + 4 => 'la fayette.jpeg', + ); + $path = drupal_get_path('module', 'feeds_tests') . '/feeds/assets'; + foreach ($images as &$image) { + $image = file_create_url("$path/$image"); + } + drupal_add_http_header('Content-Type', 'text/plain; charset=utf-8'); + print theme('feeds_tests_files', array('files' => $images)); +} + +/** + * Implements hook_feeds_processor_targets_alter(). */ function feeds_tests_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { $targets['test_target'] = array( @@ -96,7 +120,7 @@ function feeds_tests_feeds_processor_targets_alter(&$targets, $entity_type, $bun * @see my_module_set_target() */ function feeds_tests_mapper_set_target($source, $entity, $target, $value, $mapping) { - $entity->body['und'][0]['value'] = serialize($mapping); + $entity->body['und'][0]['value'] = serialize($mapping); } /** @@ -106,10 +130,10 @@ function feeds_tests_mapper_set_target($source, $entity, $target, $value, $mappi */ function feeds_tests_mapper_summary($mapping, $target, $form, $form_state) { $options = array( - 'option1' => t('Option 1'), + 'option1' => t('Option 1'), 'option2' => t('Another Option'), - 'option3' => t('Option for select'), - 'option4' => t('Another One') + 'option3' => t('Option for select'), + 'option4' => t('Another One'), ); $items = array(); @@ -139,7 +163,7 @@ function feeds_tests_mapper_summary($mapping, $target, $form, $form_state) { return drupal_render($list); } -/* +/** * Provides the form with mapper settings. */ function feeds_tests_mapper_form($mapping, $target, $form, $form_state) { @@ -181,4 +205,3 @@ function feeds_tests_mapper_form($mapping, $target, $form, $form_state) { ), ); } -