insert/upsert data to skuid image attachment field

  • 1
  • Question
  • Updated 3 months ago
  • Answered
I have a skuid image attachment lookup field on an object, and I have some external data that I'd like to migrate via the Salesforce Data loader. Any good instructions on how to set up that migration?

I know how to insert data to the salesforce "Attachments" object... but how can I insert to the skuid__Image__c object in such a way that the images will be correctly attached to my custom object via the lookup field?

Do I need to first do an insert to the skuid__Image__c object, and then find a way to make the connection via my lookup field on the parent object? Any recommendations?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb

Posted 4 months ago

  • 1
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Looks like the structure is even more complicated than I thought...


The image is actually in the salesforce Attachment object, with a parent of the skuid__Image__c object.

So, I need to:
  1. Insert create a skuid__Image__c  record for each image
  2. Insert an Attachment to actually upload the image,
  3. Somehow update the lookup field in my custom object that points to the skuid__Image__c File record?

We can't see what's happening behind the scenes in skuid's triggers, so I want to be sure that some kind of process like this is going to work before I start just trying things.

Can anyone at Skuid confirm?
Are there best practices for migrating files for use with the skuid file upload component in 'attach to a field' mode?
(Edited)
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
Over a year ago, we migrated documents and images from our legacy system using SSIS. We used an upsert to our custom object and an insert to Attachment. We did not touch skuid__image__c; perhaps we should have, but the migration worked.

Code is still available, though I would not wish SSIS on anybody.

Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
I'm all about it working! 

Mike, can you explain how you mapped fields for that migration?
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
In the first pass against the legacy images, we did the upsert to Child_Docs__c. One field was an ExternalId referencing the primary key of the image in the legacy system. We also mapped the parent key in the legacy record to the parent id (master-detail relationship) in Child_Docs__c. Other mappings included a document name, type, and date as simple custom fields. The Child Docs object has a lookup relationship field to File -- oh, that IS skuid__image__c! I believe it gets populated through the action of the external id.

The second pass through the legacy images performs an Insert (Create) to Attachment. It maps the image to Body, fixed strings to ContentType ('image/jpeg') and Name ('Photo'), and that primary key to parentId.

As I recall, and I didn't fully understand it at the time, it is the matching of the External Id values that created the links that were needed in the Lookup relationship.

Does that shed any more light? It still seems like a bit of smoke and mirrors were involved.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
thanks, Mike... I think it's getting a little bit clearer.

In your upsert to Child_Docs__c, did you set a value for the File lookup field?
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
I stand corrupted, Matt. The short answer to your question is No.

I have reviewed the SSIS process and the updated/inserted data. It turns out we pointed the Attachment records directly to the Child Docs record via ParentId, and we bypassed the skuid__images__c object completely.

The "magic" of the ExternalId designator is that the Attachment.ParentId value was an external key that the insert engine matched to the Child Docs record. The ParentId was set to an Id value without the routine having to read it from Child Docs.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
OK,  thanks, Mike...

So, in that situation are you able to access the migrated data via a skuid upload component in 'field on a model' mode? If you bypassed the skuid__images__c object completely, I'm thinking not?
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
The short answer, again: No

Those Child Docs records are differentiated in a separate model, with a child relationship template to Attachments, and displayed in a field editor where the template has this HTML:

<a href='/servlet/servlet.FileDownload?file={{{Id}}}' 

onclick="window.open(this.href, 'Client Photo',
'left=100,top=100,width=300,height=300,toolbar=1,resizable=0'); return false;" >View Legacy Photo</a>
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Ah, I see. Thanks, Mike. I suppose that could be a valid work-around for me. I'd really like to migrate our legacy image data so that it can actually be used by the File Upload component in Field on Record mode.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Skuid,

What do you recommend as the best way to migrate image data so that it can be used by the File Upload component with an "In Field on Record" storage location?


Photo of Bill McCullough

Bill McCullough, Champion

  • 12,436 Points 10k badge 2x thumb
Matt,

Two things for you.  The Body field on the Attachment record is a Base64 encoded 'string'.  If Skuid is using the same encoding scheme.  You may be able to move this 'string' to your custom field on your custom object.

The second thing is an example page that shows how to store the Id of a Content Document on an object and use this to render an image.  This page is based on the Contact object and Salesforce Files (ContentDocumentLink).  I am using the Contact Description field to store the Id of the 'LastPublishedVersion' of the file I uploaded.  The Image component uses a Salesforce page to render the image.  This is the url i am using for the image component:

/sfc/servlet.shepherd/version/download/{{{ContentDocument.LatestPublishedVersionId}}}

If you click the image, you can select a new one from any of the Content Documents you have uploaded.  Use the New Contact Image 'button' (file upload component) to both add a new file and replace the image.  When viewing the list of files, I am showing a thumbnail of the images.  There is a row action to make that image the current image for the Contact.  Note that this is all declarative.

If you use this design, you can upload your images as Salesforce Files and then follow up with an update that pulls the ContentDocument.LatestPublishedVersionId to update your custom field on your custom object.

Thanks,

Bill

<skuidpage showheader="true" personalizationmode="server" showsidebar="true" tabtooverride="Contact">
    <models>
        <model id="Contact" datasource="salesforce" createrowifnonefound="false" query="true" sobject="Contact" limit="1">
            <fields>
                <field id="FirstName"/>
                <field id="LastName"/>
                <field id="CreatedDate"/>
                <field id="MailingCity"/>
                <field id="MailingStreet"/>
                <field id="MailingState"/>
                <field id="MailingCountry"/>
                <field id="MailingPostalCode"/>
                <field id="LastModifiedDate"/>
                <field id="Phone"/>
                <field id="Email"/>
                <field id="Id"/>
                <field id="AccountId"/>
                <field id="Account.Name"/>
                <field id="Birthdate"/>
                <field id="Description"/>
            </fields>
            <conditions>
                <condition type="param" value="id" enclosevalueinquotes="true" field="Id" operator="="/>
            </conditions>
            <actions/>
        </model>
        <model id="ContentDocumentLink" datasource="salesforce" createrowifnonefound="false" query="true" sobject="ContentDocumentLink" limit="20">
            <fields>
                <field id="ContentDocumentId"/>
                <field id="ContentDocument.Title"/>
                <field id="LinkedEntityId"/>
                <field id="LinkedEntity.Name"/>
                <field id="ContentDocument.LatestPublishedVersionId"/>
                <field id="ContentDocument.LatestPublishedVersion.Title"/>
                <field id="Id"/>
                <field id="ContentDocument.Id"/>
                <field id="ContentDocument.LatestPublishedVersion.Id"/>
            </fields>
            <conditions>
                <condition type="modelmerge" value="" model="Contact" enclosevalueinquotes="true" field="LinkedEntityId" operator="=" mergefield="Id" novaluebehavior="noquery" fieldtargetobjects="Contact"/>
            </conditions>
            <actions/>
        </model>
        <model id="LastUploaded" datasource="salesforce" createrowifnonefound="false" query="false" sobject="ContentDocumentLink" limit="1" orderby="SystemModstamp DESC">
            <fields>
                <field id="ContentDocumentId"/>
                <field id="ContentDocument.Title"/>
                <field id="LinkedEntityId"/>
                <field id="LinkedEntity.Name"/>
                <field id="ContentDocument.LatestPublishedVersionId"/>
                <field id="ContentDocument.LatestPublishedVersion.Title"/>
                <field id="Id"/>
                <field id="ContentDocument.Id"/>
                <field id="SystemModstamp"/>
            </fields>
            <conditions>
                <condition type="modelmerge" value="" model="Contact" enclosevalueinquotes="true" field="LinkedEntityId" operator="=" mergefield="Id" novaluebehavior="noquery" fieldtargetobjects="Contact"/>
            </conditions>
            <actions/>
        </model>
        <model id="Selected" query="true" createrowifnonefound="true" datasource="Ui-Only" processonclient="true">
            <fields>
                <field id="CurrentIndex" displaytype="DOUBLE" label="CurrentIndex" length="3" defaultvaluetype="fieldvalue" ogdisplaytype="TEXT" precision="3" scale="0"/>
            </fields>
            <conditions/>
            <actions/>
        </model>
    </models>
    <components>
        <pagetitle uniqueid="sk-2nnOO--83" model="Contact">
            <maintitle>
                <template>{{FirstName}} {{LastName}}</template>
            </maintitle>
            <subtitle>
                <template>{{Model.label}}</template>
            </subtitle>
            <actions>
                <action type="savecancel" uniqueid="sk-zZmyt-88" window="self">
                    <models>
                        <model>ContentDocumentLink</model>
                    </models>
                    <savehotkeys>
                        <hotkey key="s" modifiers="ctrl"/>
                    </savehotkeys>
                </action>
            </actions>
        </pagetitle>
        <tabset uniqueid="sk-2nnZAZ-122" renderas="" defertabrendering="true" rememberlastusertab="true">
            <tabs>
                <tab name="Demographics">
                    <components>
                        <grid uniqueid="sk-1JOn4K-289">
                            <divisions>
                                <division verticalalign="top" behavior="specified" width="250px">
                                    <components>
                                        <image source="url" uniqueid="sk-2om0aY-1786" datasource="salesforce" behavior="button" model="Contact" url="/sfc/servlet.shepherd/version/download/{{{Description}}}" alt="Click to select another image">
                                            <styles>
                                                <styleitem type="itemsize" width="auto" height="auto"/>
                                                <styleitem type="border"/>
                                            </styles>
                                            <interactions>
                                                <interaction type="tap">
                                                    <action type="requeryModel" model="ContentDocumentLink" behavior="standard"/>
                                                    <action type="showPopup">
                                                        <popup title="Select Image" width="70%">
                                                            <components>
                                                                <deck searchmethod="server" searchbox="false" columngutter=".75em" rowgutter=".75em" model="ContentDocumentLink" filtersposition="top" filterswidth="150px" showsavecancel="false" behavior="flex" verticalalign="top" ratio="1" minwidth="150px" uniqueid="sk-2oqyo7-658" buttonposition="" pagesize="5" emptysearchbehavior="query">
                                                                    <components>
                                                                        <wrapper uniqueid="sk-2or6aG-702">
                                                                            <components>
                                                                                <image source="url" uniqueid="sk-2or1Bk-683" datasource="salesforce" behavior="none" model="ContentDocumentLink" url="/sfc/servlet.shepherd/version/download/{{{ContentDocument.LatestPublishedVersionId}}}">
                                                                                    <styles>
                                                                                        <styleitem type="itemsize" width="custom">
                                                                                            <styles>
                                                                                                <styleitem property="width" value="350px"/>
                                                                                            </styles>
                                                                                        </styleitem>
                                                                                        <styleitem type="border"/>
                                                                                    </styles>
                                                                                    <conditions>
                                                                                        <condition type="contextrow" field="Id" mergefield="Id"/>
                                                                                    </conditions>
                                                                                </image>
                                                                            </components>
                                                                            <styles>
                                                                                <styleitem type="background"/>
                                                                                <styleitem type="border"/>
                                                                                <styleitem type="size" width="custom" height="collapse">
                                                                                    <styles>
                                                                                        <styleitem property="max-width" value="300px"/>
                                                                                    </styles>
                                                                                </styleitem>
                                                                            </styles>
                                                                        </wrapper>
                                                                    </components>
                                                                    <massactions/>
                                                                    <interactions>
                                                                        <interaction type="tap" direction="either">
                                                                            <action type="updateRow" fieldmodel="Contact" affectedrows="context" field="Description" enclosevalueinquotes="true" value="{{ContentDocument.LatestPublishedVersionId}}">
                                                                                <models/>
                                                                                <popup title="{{Model.label}}: {{Name}}" width="80%">
                                                                                    <components/>
                                                                                </popup>
                                                                            </action>
                                                                            <action type="save" rollbackonanyerror="true">
                                                                                <models>
                                                                                    <model>Contact</model>
                                                                                </models>
                                                                                <onerroractions>
                                                                                    <action type="blockUI" message="There was an error" timeout="3000"/>
                                                                                    <action type="unblockUI" message="There was an error" timeout="3000"/>
                                                                                </onerroractions>
                                                                            </action>
                                                                            <action type="closeTopmostPopup"/>
                                                                        </interaction>
                                                                    </interactions>
                                                                    <actions/>
                                                                    <styles>
                                                                        <styleitem type="border"/>
                                                                    </styles>
                                                                    <searchfields/>
                                                                </deck>
                                                            </components>
                                                        </popup>
                                                    </action>
                                                </interaction>
                                            </interactions>
                                        </image>
                                        <file storeas="contentdocumentwithrecord" displayas="filename" uniqueid="sk-2oxn1B-383" datasource="salesforce" model="Contact" label="New Contact Image">
                                            <uploadsuccessactions>
                                                <action type="requeryModel" model="LastUploaded" behavior="standard"/>
                                                <action type="updateRow" fieldmodel="Contact" affectedrows="context" field="Description" enclosevalueinquotes="true" value="{{{$Model.LastUploaded.data.0.ContentDocument.LatestPublishedVersionId}}}"/>
                                                <action type="save">
                                                    <models>
                                                        <model>Contact</model>
                                                    </models>
                                                </action>
                                            </uploadsuccessactions>
                                            <uploadfailureactions/>
                                        </file>
                                    </components>
                                </division>
                                <division verticalalign="top" behavior="flex" ratio="1" minwidth="100px">
                                    <components>
                                        <basicfieldeditor uniqueid="sk-2nnOO--84" model="Contact" mode="read" buttonposition="" showsavecancel="false" layout="" showheader="true">
                                            <columns>
                                                <column width="50%">
                                                    <sections>
                                                        <section title="Basics" collapsible="no">
                                                            <fields>
                                                                <field id="FirstName" type="" uniqueid="sk-zZmzM-96" valuehalign=""/>
                                                                <field id="LastName" uniqueid="sk-zZmzP-98"/>
                                                                <field id="Phone" uniqueid="sk-zaxUD-370" cssclass="" snippet="businessPhoneRender"/>
                                                                <field id="Email" type="" uniqueid="sk-13zjI1-245" valuehalign=""/>
                                                                <field id="AccountId" uniqueid="sk-1VHq5D-261" pagesize="5" optionsource="" redirecttype="datasourcedefault">
                                                                    <searchfields soslfields="All Fields" usesosl="true"/>
                                                                    <filters/>
                                                                    <renderconditions logictype="and" onhidedatabehavior="keep"/>
                                                                    <enableconditions/>
                                                                </field>
                                                                <field id="Birthdate" uniqueid="sk-22tbDe-276"/>
                                                            </fields>
                                                        </section>
                                                        <section title="Image" collapsible="no">
                                                            <fields>
                                                                <field uniqueid="sk-2ovbdg-953" id="Description"/>
                                                            </fields>
                                                        </section>
                                                    </sections>
                                                </column>
                                                <column width="50%">
                                                    <sections>
                                                        <section title="Address" collapsible="no">
                                                            <fields>
                                                                <field id="MailingStreet" uniqueid="sk-zZmzp-115"/>
                                                                <field id="MailingCity" uniqueid="sk-zZmzq-117"/>
                                                                <field id="MailingState" uniqueid="sk-zZmzr-119"/>
                                                                <field id="MailingPostalCode" uniqueid="sk-zZmzv-121"/>
                                                                <field id="MailingCountry" uniqueid="sk-zZmzz-123"/>
                                                            </fields>
                                                        </section>
                                                    </sections>
                                                </column>
                                            </columns>
                                        </basicfieldeditor>
                                    </components>
                                </division>
                            </divisions>
                            <styles>
                                <styleitem type="background" bgtype="none"/>
                            </styles>
                        </grid>
                    </components>
                </tab>
                <tab name="Files" loadlazypanels="true">
                    <components>
                        <file datasource="salesforce" uniqueid="sk-1Yfw5z-376" model="Contact" displayas="filename" storeas="contentdocumentwithrecord">
                            <uploadsuccessactions>
                                <action type="requeryModels" behavior="standard" model="ContactFeed">
                                    <models>
                                        <model>ContentDocumentLink</model>
                                    </models>
                                </action>
                            </uploadsuccessactions>
                            <uploadfailureactions/>
                            <renderconditions logictype="and"/>
                        </file>
                        <skootable uniqueid="sk-1MPZvX-293" model="ContentDocumentLink" mode="read" buttonposition="" createrecords="true" pagesize="10" showexportbuttons="false" searchbox="true" searchmethod="server" showerrorsinline="true" showsavecancel="false" showconditions="true" allowcolumnreordering="true" responsive="true" alwaysresetpagination="false">
                            <fields>
                                <field id="ContentDocumentId" uniqueid="fi-1MPhMg-312" hideable="true"/>
                                <field type="COMBO" hideable="true" uniqueid="fi-2ooSE_-1827" allowhtml="true">
                                    <label>View</label>
                 
(Edited)
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Thanks, Bill. Nice solution.

I'd really like to avoid changing the structure of my page to accommodate this data migration.

Skuid, is there any way to migrate image data so it can be used by the File Upload component?