diff --git a/mappers/text.inc b/mappers/text.inc index f163e25..545c845 100644 --- a/mappers/text.inc +++ b/mappers/text.inc @@ -44,8 +44,7 @@ function text_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam $targets[$name . ':format'] = array( 'name' => check_plain($instance['label']) . ': Format', 'callback' => 'text_feeds_set_target', - 'description' => t('The @label field\'s format of the entity. This will overwrite the default or configured format fo the field.', array('@label' => $instance['label'])), - 'real_target' => $name, + 'description' => t('The @label field\'s format of the entity. This will overwrite the default or configured format of the field.', array('@label' => $instance['label'])), 'summary_callback' => 'text_feeds_format_summary_callback', 'form_callback' => 'text_feeds_format_form_callback', ); @@ -62,17 +61,16 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val if (isset($source->importer->processor->config['input_format'])) { $default_format = $source->importer->processor->config['input_format']; - // Add in default values, depending on target. if ($column === 'value') { + // Add default target configuration for the "value" part of text fields. $mapping += array( 'format' => $default_format, ); } - elseif ($column === 'format') { - $mapping += array( - 'allowed_formats' => array($default_format), - ); - } + } + if ($column === 'format') { + // Add default target configuration for the "format" part of text fields. + $mapping += text_feeds_format_defaults($source->importer->processor); } $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array()); @@ -182,17 +180,13 @@ function text_feeds_format_summary_callback($mapping, $target, $form, $form_stat $formats = filter_formats($user); $importer = feeds_importer_load($form['#importer']); - $default_format = isset($importer->processor->config['input_format']) ? $importer->processor->config['input_format'] : filter_fallback_format(); - $mapping += array( - 'allowed_formats' => array($default_format), - ); + $mapping += text_feeds_format_defaults($importer->processor); $format_names = array(); foreach (array_filter($mapping['allowed_formats']) as $format) { $format_names[] = $formats[$format]->name; } - return t('Allowed formats: %formats', array('%formats' => implode(', ', $format_names))); } @@ -213,10 +207,7 @@ function text_feeds_format_form_callback($mapping, $target, $form, $form_state) // Processor-wide input format setting. $importer = feeds_importer_load($form['#importer']); - $default_format = isset($importer->processor->config['input_format']) ? $importer->processor->config['input_format'] : filter_fallback_format(); - $mapping += array( - 'allowed_formats' => array($default_format), - ); + $mapping += text_feeds_format_defaults($importer->processor); $config['allowed_formats'] = array( '#type' => 'checkboxes', @@ -229,3 +220,40 @@ function text_feeds_format_form_callback($mapping, $target, $form, $form_state) return $config; } + +/** + * Returns default configuration for the text format mapper. + * + * @param FeedsProcessor $processor + * The processor that is used for processing the items during import. + * The processor holds configuration for the importer's default text format, + * which is one of the text formats that should be allowed by default for the + * text format target. + * + * @return array + * The default configuration for the text format mapper. + */ +function text_feeds_format_defaults(FeedsProcessor $processor) { + $default_processor_format = isset($processor->config['input_format']) ? $processor->config['input_format'] : filter_fallback_format(); + $default_site_format = filter_fallback_format(); + + if ($default_processor_format == $default_site_format) { + // If the site's text format is the same as processor's text format only one + // text format is allowed by default. + return array( + 'allowed_formats' => array( + $default_processor_format, + ), + ); + } + else { + // If the site's text format is different from the processor's text format + // both text formats are allowed by default. + return array( + 'allowed_formats' => array( + $default_site_format, + $default_processor_format, + ), + ); + } +} diff --git a/tests/feeds/nodes_text_format.csv b/tests/feeds/nodes_text_format.csv index d326b3d..89de894 100644 --- a/tests/feeds/nodes_text_format.csv +++ b/tests/feeds/nodes_text_format.csv @@ -1,3 +1,4 @@ -"guid","title","body","body_format" -1,"Lorem ipsum","Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.","allowed_text_format" -2,"Ut wisi enim ad minim veniam","Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.","non_allowed_text_format" +"guid","title","body","body_format","alpha","alpha_format" +1,"Lorem ipsum","Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.","allowed_text_format","Lorem","non_allowed_text_format" +2,"Ut wisi enim ad minim veniam","Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.","non_allowed_text_format","Ut wisi","allowed_text_format" +3,"Duis autem vel eum iriure dolor","Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.","plain_text" diff --git a/tests/feeds_mapper_format.test b/tests/feeds_mapper_format.test index 838efd9..a038044 100644 --- a/tests/feeds_mapper_format.test +++ b/tests/feeds_mapper_format.test @@ -18,14 +18,36 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { } /** - * Basic test mapping to text format. + * Basic test for mapping to text format. + * + * This test does the following: + * 1. It ensures that mapping to text format works as intended. + * 2. It ensures that mapping to text format has precedence on the target + * configuration of text fields itself, which also can touch the text + * format of text fields. To ensure this, there is mapped to two format + * targets, as well as to two text field targets. For the first text field + * the format target is defined first, while for the second text field the + * text field itself comes first. This way we've taken care of that the + * order of mappings doesn't matter. + * 3. It ensures that not every format specified in the source is accepted. + * This is done to avoid security issues. Imagine someone would specify + * "php" as the format to use in the source. If that format can not be + * blocked, then someone could (unintentionally) import executable php + * code. */ public function test() { // Create a content type. - $typename = $this->createContentType(); + $typename = $this->createContentType(array(), array( + 'alpha' => array( + 'type' => 'text', + 'instance_settings' => array( + 'instance[settings][text_processing]' => 1, + ), + ), + )); - // Create two filter formats. One that may be used and one that - // may NOT be used. + // Create two filter formats. One that may be used and one that may NOT be + // used. $edit = array( 'format' => 'allowed_text_format', 'name' => 'Allowed text format', @@ -41,12 +63,11 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { // Create and configure importer. $this->createImporterConfiguration(); - $this->setSettings('syndication', NULL, array('content_type' => '')); - $this->setPlugin('syndication', 'FeedsFileFetcher'); $this->setPlugin('syndication', 'FeedsCSVParser'); $this->setSettings('syndication', 'FeedsNodeProcessor', array('bundle' => $typename)); - + // We map to two format targets here, to ensure that the order of mappings + // does not matter. $this->addMappings('syndication', array( 0 => array( 'source' => 'title', @@ -56,7 +77,6 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { 'source' => 'body_format', 'target' => 'body:format', 'allowed_formats' => array( - filter_fallback_format() => filter_fallback_format(), 'allowed_text_format' => 'allowed_text_format', ), ), @@ -64,21 +84,162 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { 'source' => 'body', 'target' => 'body', ), + 3 => array( + 'source' => 'alpha', + 'target' => 'field_alpha', + ), + 4 => array( + 'source' => 'alpha_format', + 'target' => 'field_alpha:format', + 'allowed_formats' => array( + 'allowed_text_format' => 'allowed_text_format', + ), + ), )); // Import csv file. $this->importFile('syndication', $this->absolutePath() . '/tests/feeds/nodes_text_format.csv'); - $this->assertText('Created 2 nodes'); + $this->assertText('Created 3 nodes'); - // Assert that the body field of both nodes got the expected text format. + // Assert that the body and alpha fields of the nodes got the expected text + // format. Note that of the first node, the body field uses an allowed + // format, but the alpha field does not. For the second node it is the exact + // opposite. $node = node_load(1); $this->assertEqual('allowed_text_format', $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 1 got the expected format.'); + $this->assertEqual(filter_fallback_format(), $node->field_alpha[LANGUAGE_NONE][0]['format'], 'The alpha field of node 1 got the expected format.'); $node = node_load(2); $this->assertEqual(filter_fallback_format(), $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 2 got the expected format.'); + $this->assertEqual('allowed_text_format', $node->field_alpha[LANGUAGE_NONE][0]['format'], 'The alpha field of node 2 got the expected format.'); } /** - * Tests the filter formats a user has access to. + * Tests if only the default text formats are accepted when format mapper is + * not configured. + * + * Testing is done as such: + * 1. First, three nodes are created that have a text format other than the + * default. + * 2. An importer is created that has its default format is set to + * 'allowed_text_format'. + * Mapping against body format is done, but without any configuration. + * 3. Importing is done. + * 4. Asserted is that the three updated nodes have the expected formats: + * - Node 1 should have 'allowed_text_format', since that one is allowed + * because of the importer's default setting. + * - The format of node 2 should be unchanged, because the source specified + * a now-allowed format. + * - Node 3 should have 'plain_text', because that is the site default text + * format. + */ + public function testDefaults() { + // Create a content type. + $typename = $this->createContentType(); + + // Create a filter format to use for the three nodes to create. + $format_init = drupal_strtolower($this->randomName()); + $edit = array( + 'format' => $format_init, + 'name' => $this->randomName(), + // Authenticated users. + 'roles[2]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); + + // Create three nodes to update. Set the text format of the body field. + // The titles are taken from nodes_text_format.csv. + // Note that $nodes variable is used at the end of this test to assert that + // the body values did not change as there is not being mapped to body. + $nodes = array( + array( + 'title' => 'Lorem ipsum', + 'body' => self::randomString(), + ), + array( + 'title' => 'Ut wisi enim ad minim veniam', + 'body' => self::randomString(), + ), + array( + 'title' => 'Duis autem vel eum iriure dolor', + 'body' => self::randomString(), + ), + ); + foreach ($nodes as $node_data) { + $this->drupalCreateNode(array( + 'type' => $typename, + 'title' => $node_data['title'], + 'body' => array( + LANGUAGE_NONE => array( + array( + 'value' => $node_data['body'], + 'format' => $format_init, + ), + ), + ), + )); + } + + // Assert that the created nodes have the expected format assigned. + $node = node_load(1, NULL, TRUE); + $this->assertEqual($format_init, $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 1 got the expected format.'); + $node = node_load(2, NULL, TRUE); + $this->assertEqual($format_init, $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 2 got the expected format.'); + $node = node_load(3, NULL, TRUE); + $this->assertEqual($format_init, $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 3 got the expected format.'); + + // Create a filter format to use as default for the importer. + $edit = array( + 'format' => 'allowed_text_format', + 'name' => 'Allowed text format', + // Authenticated users. + 'roles[2]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); + + // Create and configure importer. + $this->createImporterConfiguration(); + $this->setPlugin('syndication', 'FeedsFileFetcher'); + $this->setPlugin('syndication', 'FeedsCSVParser'); + $this->setSettings('syndication', 'FeedsNodeProcessor', array( + 'bundle' => $typename, + 'input_format' => 'allowed_text_format', + 'update_existing' => 2, + )); + $this->addMappings('syndication', array( + 0 => array( + 'source' => 'title', + 'target' => 'title', + 'unique' => 1, + ), + 1 => array( + 'source' => 'body_format', + 'target' => 'body:format', + ), + )); + + // Import csv file. + $this->importFile('syndication', $this->absolutePath() . '/tests/feeds/nodes_text_format.csv'); + $this->assertText('Updated 3 nodes'); + + // Assert that only the text format of nodes 1 and 3 had changed. Also + // assert that the body field did not change. + $node = node_load(1, NULL, TRUE); + $this->assertEqual('allowed_text_format', $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 1 got the expected format.'); + $this->assertEqual($nodes[0]['body'], $node->body[LANGUAGE_NONE][0]['value'], 'The value of the body field of node 1 did not change.'); + $node = node_load(2, NULL, TRUE); + $this->assertEqual($format_init, $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 2 got the expected format.'); + $this->assertEqual($nodes[1]['body'], $node->body[LANGUAGE_NONE][0]['value'], 'The value of the body field of node 2 did not change.'); + $node = node_load(3, NULL, TRUE); + $this->assertEqual('plain_text', $node->body[LANGUAGE_NONE][0]['format'], 'The body field of node 3 got the expected format.'); + $this->assertEqual($nodes[2]['body'], $node->body[LANGUAGE_NONE][0]['value'], 'The value of the body field of node 3 did not change.'); + } + + /** + * Tests the filter formats an user has access to. + * + * An user that has no permission to use some of the text formats on the + * website, should not be able to choose these as an allowed format to accept + * from the source. */ public function testWithLimitedPrivileges() { // Create content type with a field that uses a text format. @@ -119,10 +280,6 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { // Create importer and ensure the user can use the first format. $this->createImporterConfiguration(); - $this->setSettings('syndication', NULL, array( - 'content_type' => '', - 'import_period' => FEEDS_SCHEDULE_NEVER, - )); $this->setPlugin('syndication', 'FeedsFileFetcher'); $this->setPlugin('syndication', 'FeedsCSVParser'); $this->setSettings('syndication', 'FeedsNodeProcessor', array('bundle' => $typename)); @@ -140,4 +297,51 @@ class FeedsMapperFormatTestCase extends FeedsMapperTestCase { $this->assertNoFieldByName('config[0][settings][allowed_formats][' . $format2 . ']', NULL, format_string('Text format %format can NOT be chosen.', array('%format' => $format2))); } + /** + * Tests if format mapper is only available for fields that have filtered text + * enabled. + * + * Only text fields that are configured to be filtered text have use for a + * text format. Other text fields can only be plain text. In this test it is + * ensured that there is no format target for text fields that can only be + * plain text. + */ + public function testMapperAvailability() { + // Create content type with three fields. Two that support text formats + // and one that doesn't. + $typename = $this->createContentType(array(), array( + 'alpha' => array( + 'type' => 'text', + 'instance_settings' => array( + 'instance[settings][text_processing]' => 1, + ), + ), + 'beta' => array( + 'type' => 'text_long', + 'instance_settings' => array( + 'instance[settings][text_processing]' => 1, + ), + ), + 'gamma' => array( + 'type' => 'text', + 'instance_settings' => array( + 'instance[settings][text_processing]' => 0, + ), + ), + )); + + // Create importer. + $this->createImporterConfiguration(); + $this->setSettings('syndication', 'FeedsNodeProcessor', array('bundle' => $typename)); + + // Get mapping targets from importer. + $importer = feeds_importer('syndication'); + $targets = $importer->processor->getMappingTargets(); + + // Assert availability of format mapper. + $this->assertTrue(isset($targets['field_alpha:format']), 'A format mapper for the alpha field exists.'); + $this->assertTrue(isset($targets['field_beta:format']), 'A format mapper for the beta field exists.'); + $this->assertFalse(isset($targets['field_gamma:format']), 'A format mapper for the gamma field does not exists.'); + } + }