Unable to edit object with attachment with content.modify

Enonic version: Linux
OS: 6.12.0

Hi !
I found strange issue with attachments in content objects. We have a service to create or update content objects in Enonic by POST requests. And it doesn’t work for objects that have attachment. In this example PDF file. During update process script updates only changed attributes in “data” sub-object.
Please check this part of code:

var updatedObjectData = …;
var currentObject = …;

result = libs.content.modify({
key: objectID,
editor: profileEditor,
branch: ‘draft’
});

function profileEditor( c ) {
c.data = currentObject.data;
for( var key in updatedObjectData ) {
c.data[key] = updatedObjectData[key];
}
return c;
}

And during this action script returns an exception:

Value of type [java.lang.String] cannot be converted to [Reference]: NodeId format incorrect: myfile.pdf

It seems that Enonic thinks that attachments should be stored as a reference with nodeID. But in our case attachments are inline.

How we can fix this issue ?

Thanks !

Currently you cannot make changes to attachments using the content.modify function. We are implementing new functions for working with content attachments. They will be included in the next release.

If you want to make changes to the attachments now, you can do it by using the node api.

What do you mean that your attachments are inline?
Can you explain what you wanted to update in the attachment? So I can provide a more concrete example using node API.

Hi @aro,

I don’t need to update attachment. All I need is to update attributes inside “data” object.

Ok. Then try skipping the “attachment” property in the modify editor function:

function profileEditor( c ) {
  c.data = currentObject.data;
  for( var key in updatedObjectData ) {
    if (key === 'attachment') {
       continue;
    }
    c.data[key] = updatedObjectData[key];
  }
  return c;
}

This field doesn’t exist in updatedObjectData at all. And I can’t set it to null in c.data because this action will remove attachment. In updatedObjectData I have only name, description and modification date.

How does the content type look like? Is there any AttachmentUploader input in the content type form?

The problem seems to be that the content has a property of type Reference (probably it’s from AttachmentUploader input), but the modify call tries to set a value of type String.

If you know which property it is, you can try to convert it to Reference before setting it in c.data[key].

var valueLib = require('/lib/xp/value');
if (key === 'myProperty') {
  c.data[key] = valueLib.reference( updatedObjectData[key] );
}

This is a part of content type:

    <item-set name="ATTACHMENTS">
      <label>Attachment</label>
      <items>
        <input type="TextLine" name="ATTACHMENTS_TITLE">
          <label>Title</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
        <input type="AttachmentUploader" name="ATTACHMENTS_URL">
          <label>File</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
      </items>
      <occurrences minimum="0" maximum="3"/>
    </item-set>

And this is how it’s stored in Enonic:

"ATTACHMENTS": {
        "ATTACHMENTS_TITLE": "Test file",
        "ATTACHMENTS_URL": "testfile.pdf"
    },

The problem is that the property of the AttachmentUploader (“ATTACHMENTS_URL”) is of type Reference. When you get a content using the JavaScript content library, the property is converted to String. When you then try to set the value in the property in the modify function, the content data is validated according to the content type and it fails.

We will look into the contentLib functions and try to solve it for next version.
There are 2 workarounds for now:

  • avoid setting the property that you know is from an AttachmentUploader (as I mentioned above)
  • use the node library (maybe also the value library) to do the modify

Hi @aro,

currently there is only one way to modify content - set data.ATTACHMENTS to null. Is this correct ? We don’t need to change or set value for this attribute. We need to change other attributes in “data” object.

Any news or ideas how to solve this ? :slight_smile: As far as I understand now it’s not possible to modify content that has an attribute with type “AttachmentUploader”.

I tried to modify a content with an attachment as you explained but I don’t get the “NodeId format incorrect” error.

Can you make an export of the content with the attachment that fails to update? And send me the export, the content-type xml, and the JavaScript controller or code that makes the update, so I can reproduce it?

You can also send it via our Slack channel .

Hi @aro,

I was able to reproduce this problem with this code:

    var user = libs.content.get({key: '5faf0711-b1b8-4ec9-abeb-3d1afb0687d7'});    
    var preparedUserData = {};
    preparedData['EMPLOYEE_PHONE'] = '123456';
    //preparedData['EMPLOYEE_PHONE'] = '46888266';

    // check modify content
    result = libs.content.modify({
        key: user._id,
        editor: profileEditor,
        branch: 'draft'
    });    

    function profileEditor( c ) {
        c.displayName = user.displayName;
        c.data = user.data;
        for( var key in preparedData ) {
            c.data[key] = preparedData[key];
        }
        return c;
    } 

And this is our content type:

<?xml version="1.0" encoding="UTF-8"?>
<content-type>
  <display-name>Employee</display-name>  
  <super-type>base:structured</super-type>
  <form>
    <input type="TextLine" name="EMPLOYEE_FIRST_NAME">
      <label>First Name</label>
      <occurrences minimum="1" maximum="1"/>
    </input>
    <input type="TextLine" name="EMPLOYEE_LAST_NAME">
      <label>Last Name</label>
      <occurrences minimum="1" maximum="1"/>
    </input>
    <input type="HtmlArea" name="EMPLOYEE_INTRO">
      <label>Biografi</label>
      <occurrences minimum="0" maximum="1"/>
      <config>
        <exclude>image | table | link | unlink | macro</exclude>
      </config>
    </input>
    <input name="EMPLOYEE_IMAGE" type="ImageSelector">
      <label>Image</label>
      <occurrences minimum="0" maximum="1"/>
      <config>
        <allowPath>./*</allowPath>
      </config>
    </input>
    <input name="EMPLOYEE_DEPARTMENT" type="ContentSelector">
      <label>Employee's department</label>
      <occurrences minimum="0" maximum="1"/>
      <config>
        <allowContentType>department</allowContentType>
        <relationshipType>system:reference</relationshipType>
        <allowPath>*</allowPath>
      </config>
    </input>
    <item-set name="EMPLOYEE_LINKS">
      <label>Link</label>
      <items>
        <input type="TextLine" name="EMPLOYEE_LINKS_TITLE">
          <label>Title</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
        <input type="TextLine" name="EMPLOYEE_LINKS_URL">
          <label>URL</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
      </items>
      <occurrences minimum="0" maximum="5"/>
    </item-set>
    <item-set name="EMPLOYEE_ATTACHMENTS">
      <label>Attachment</label>
      <items>
        <input type="TextLine" name="EMPLOYEE_ATTACHMENTS_TITLE">
          <label>Title</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
        <input type="AttachmentUploader" name="EMPLOYEE_ATTACHMENTS_URL">
          <label>File</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
      </items>
      <occurrences minimum="0" maximum="3"/>
    </item-set>
    <input type="TextLine" name="EMPLOYEE_PHONE">
      <label>Phone</label>
      <occurrences minimum="1" maximum="1"/>
    </input>
    <input type="TextLine" name="EMPLOYEE_EMAIL">
      <label>Email</label>
      <occurrences minimum="1" maximum="1"/>
    </input>
    <input name="EMPLOYEE_CITY" type="ContentSelector">
      <label>Kontorsted</label>
      <occurrences minimum="1" maximum="1"/>
      <config>
        <allowContentType>address</allowContentType>
        <relationshipType>system:reference</relationshipType>
        <allowPath>*</allowPath>
      </config>
    </input>    
    <inline mixin="tags"/>
  </form>
</content-type>

During execution of libs.content.modify I get this error:

13:40:11.966 ERROR c.e.x.p.i.e.ExceptionRendererImpl - com.enonic.xp.resource.ResourceProblemException: Value of type [java.lang.String] cannot be converted to [Reference]: NodeId format incorrect: myfile.pdf
com.enonic.xp.web.WebException: com.enonic.xp.resource.ResourceProblemException: Value of type [java.lang.String] cannot be converted to [Reference]: NodeId format incorrect: myfile.pdf
at com.enonic.xp.web.impl.exception.ExceptionMapperImpl.map(ExceptionMapperImpl.java:32)
at com.enonic.xp.portal.handler.BasePortalHandler.handleError(BasePortalHandler.java:62)
at com.enonic.xp.portal.handler.BasePortalHandler.doHandle(BasePortalHandler.java:54)
at com.enonic.xp.web.handler.BaseWebHandler.handle(BaseWebHandler.java:71)
at com.enonic.xp.web.impl.handler.WebHandlerChainImpl.handle(WebHandlerChainImpl.java:30)

After some debugging we found the problem. There is a bug that makes content.modify fail if the content has an AttachmentUpload field, and the file uploaded to this field contained spaces in its name. This is because for updating the content, the value is converted to a Reference type which allows a restricted set of characters (same as NodeId).

We have registered an issue to fix the bug in the next release.

1 Like

Which version was this fixed in?
I’m having the same problem now in XP 6.15.10.

The reason I need to modify the document which contains an AttachmentUpload field with a file which contains spaces in its name is becuase uploading a pdf file with spaces in the filename to Enonic here:

Screenshot 2021-01-29 at 08.25.47

creates a content in Enonic looking like this:

{
    "_id": "082ae5a7-49fb-45f7-b805-0ce387917ed7",
    "_name": "PDF med mellomrom om æøå.pdf",
    "_path": "/PDF med mellomrom om æøå.pdf",
    "creator": "user:system:su",
    "modifier": "user:system:su",
    "createdTime": "2021-01-29T07:20:41.056Z",
    "modifiedTime": "2021-01-29T07:20:41.056Z",
    "owner": "user:system:su",
    "type": "media:document",
    "displayName": "PDF med mellomrom om æøå",
    "hasChildren": false,
    "valid": true,
    "data": {
    "media": {
        "attachment": "PDF med mellomrom om æøå.pdf"
    },
    "caption": "",
    "artist": "",
    "copyright": "",
    "tags": ""
},
    "x": {},
    "page": {},
    "attachments": {
    "PDF med mellomrom om æøå.pdf": {
        "name": "PDF med mellomrom om æøå.pdf",
        "label": "source",
        "size": 31093,
        "mimeType": "application/pdf"
    }
},
    "publish": {}
}

We can not use the _name or _path here as it is not sanitized. So my temporary solution was to try to create a service which loops through all the files and update the _name and _path with a sanitized version. But then I encountered this problem instead.
Is there a way to maybe upload pdf files which will actually sanitize the needed fields (we need to upload 200-300 files, as these are files which is already created elsewhere but need to be available in Enonic)? Or if needed, is there a solution to this problem I can use in XP6?

It should be possible to change the pdf with the content lib, its just not as simple as a single modify call.
In short: get the attachement, get the attachment stream, remove the attachement from the content, then use addAttachment to create the new updated pdfs. You might have to remove old attachment as a last step. Not sure if the attachmentStream is a refrence or not.

But you might just want to create a new bug topic/message here on discuss and explain the problem with spaces in names, and how this is a problem. That said i have not tested if this is an issue in xp7.