Problem/Motivation
Given a content type with a field of type text_with_summary
(e.g.: the body
field, which Drupal adds by default) If I map a source field to the summary field (e.g.: body:summary
), but I do not also map a source field to the main part of the field (e.g.: body
), then the main part of the field is erased upon import.
Steps to reproduce
- Download the latest version of Drupal 7 core, the latest version of the Chaos tools module (
ctools-7.x-1.11
at time of writing), and the latest version of the Job Scheduler module (job_scheduler-7.x-2.0-alpha3
at time of writing). Clone the Feeds module, branch7.x-2.x
. - Install Drupal using the minimal install profile. Enable Field UI, Feeds, Feeds UI, and their dependencies.
- Go to
/admin/structure/types
and create a content type. I named mine "Test content type" (test_content_type
) and accepted all the default options. - Note that Drupal automatically adds a
body
field to the new content type. - Go to
/node/add
and create a new node of the new content type. Enter something distinct into the Title, Summary, and Body fields. I usedLorem
for the Title,Ipsum
for the Summary, andDolor
for the Body. Take note of the node ID (mine had a node ID of1
). - Go to
/admin/structure/feeds
and create a Feeds importer. I named mine "Test importer" (test_importer
), and used the following options:- Basic settings:
- Attach to content type:
Use standalone form
- Periodic import:
Off
- Import on submission: Checked
- Process in background: Unchecked
- Attach to content type:
- Fetcher:
File upload
- Allowed file extensions:
txt csv tsv xml opml
- Immediately delete uploaded file after import: Unchecked
- Supply path to file or directory directly: Unchecked
- Upload directory:
private://feeds
- Allowed file extensions:
- Parser:
CSV parser
- Default delimiter:
,
- No headers: Unchecked
- File encoding:
UTF-8
- Default delimiter:
- Processor:
Node
Settings:- Bundle:
Test content type
- Insert new nodes:
Insert new nodes
- Update existing nodes:
Update existing nodes
- Skip hash check: Checked
- Text format:
Plain text
- Action to take when previously imported nodes are missing in the feed:
Skip non-existent nodes
- Author:
anonymous
- Authorize: Unchecked
- Expire nodes:
Never
Mapping:
Source Target Target configuration nid Node ID (nid) Used as unique. Title Title (title) Not used as unique. Summary Body: Summary (body:summary) Body Body (body) Text format: Plain text - Bundle:
- Basic settings:
- Create a CSV file on your local machine. The header row must match the Sources in the Mapping you set up earlier. The nid field must match the node ID you took note of earlier when you created the node. The rest of the data should be different from the data you entered when you created the node. For example, my CSV file was:
nid,Title,Summary,Body 1,Foo,Bar,Baz
- Go to
/import
. Use the default importer options, but upload the CSV file you created in the previous step, and click theImport
button. - When the import is complete, edit the node you created earlier. Note that the Title matches the value you put in the CSV (e.g.: mine was "Foo"). Note that the Summary matches the value you put in the CSV (e.g.: mine was "Bar"). Note that the body matches the value you put in the CSV (e.g.: mine was "Baz"). Note this likely matches your expectations.
- Edit the CSV file on your local machine. Remove the Body mapping, and change the Title and Summary to different values. For example, after editing, my CSV file was:
nid,Title,Summary 1,Fizz,Buzz
- Go to
/admin/structure/feeds
and edit the importer you created earlier. Go back to Processor -> Mappings. Remove the "Body" Mapping by checking the "Remove" checkbox in the last column of the table, and clicking theSave
button at the bottom. Verify that the Body mapping no longer exists. - Go to
/import
. Use the default importer options, but (re-)upload the CSV file you modified, and click theImport
button. - When the import is complete, edit the node you created earlier. Note that the Title matches the value you put in the CSV (e.g.: mine was "Fizz"). Note that the Summary matches the value you put in the CSV (e.g.: mine was "Buzz"). Note that the body is empty! Note that this likely does not match your expectations: I would expect it to be the value of the Body field before the import (i.e.: "Baz").
Analysis
The text_with_summary
field has three (sub-)parts: a summary
, a value
and a format
.
When text_feeds_set_target()
builds a new value for the summary
(sub-)part of the field, it modifies the entity loaded on line 280 of \FeedsProcessor::process()
(that line calls \FeedsProcessor::entityLoad()
).
In \FeedsProcessor::map()
, there is code to reset the value of a field before data is mapped to it. However, because the definition for the summary field target has a 'real_target'
property, \FeedsProcessor::map()
resets the whole field, not just the summary.
Proposed resolution
Remove the 'real_target'
property so that \FeedsProcessor::map()
doesn't reset everything in the field.
Remaining tasks
Write and upload a patch- Review and feedback
- RTBC
- Commit
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#6 | 2827244-6-set-summary-without-body-resets-body.patch | 2.14 KB | mparker17 |
|
Comments
Comment #2
mparker17A bit of further investigation suggests that the data for that field should already have been loaded on line 280 of
\FeedsProcessor::process()
(which calls\FeedsProcessor::entityLoad()
): the resulting entity has a$entity->body[LANGUAGE_NONE]
array, which is empty (i.e.: missing the deltas for the field, as well as the sub-fields in each delta).text_feeds_set_target()
simply modifies that entity, so in theory, the existing code should work if the old data was being loaded to the correct place.That means I should determine why that field does not load properly.
Comment #3
mparker17Hmm... the entity loads fine; but inside
\FeedsProcessor::map()
, there is a chunk of code that resets it to an empty array:Comment #4
mparker17The target sub-fields are defined in
text_feeds_processor_targets()
.Not sure there will be a way to fix this bug without changing the configuration of all mappers, though: as far as I can tell, all
text_with_summary
fields will need separate:value
and:summary
fields in order to prevent the field mapper from eating the:value
if the:summary
is updated. :SNow that I understand what's happening here, it is clear that this problem is closely related to #2309273: Mapping to text format — that allows mappings for the
:format
sub-field. If a patch in this issue or in #2309273: Mapping to text format is committed, it is likely that the other patch will need to be re-rolled, as they both touch roughly the same code.Comment #5
mparker17So, it turns out the solution is just to remove the
'real_target'
definition for the summary field, and that's enough for Feeds to figure out that it shouldn't stomp the other values in the field. Much simpler than I initially thought.Comment #6
mparker17Added more-comprehensive documentation.
Comment #7
MegaChriz CreditAttribution: MegaChriz as a volunteer commentedFrom an user point of view I agree that this is an issue. From a technical point of view, it is officially a single field. The same problem exists for the link field: if you only map to "field_link:title" and not to "field_link:url", the title for the link will be updated, but the url will be erased (if there previously was a value).
Removing the "real_target" fixes the issue for ":summary", but it will also set
$target_item->{'field_name:summary'} = NULL
which doesn't necessary cause direct issues, but is not nice. I haven't tested it, but I think if the field is multivalued and you want to empty the summary of all values by providing an empty string, only the first one will be emptied.Maybe we should define a new property for the target definition for this case? 'sub_field' maybe? Then, if defined, the
map()
method would only empty the values for that subfield.