Press "Enter" to skip to content

Embedded Canvas Apps for SharePoint Documents

Starting to see a few requirements coming up from clients around surfacing documents in sub grids or being able to access documents through Embedded Canvas Apps for SharePoint from Dynamics 365 CE records.

Thought I would try implementing this through the use of Flow and specifically the OOTB SharePoint connectors. The connector I’m going to be using is the “Send HTTP Request to SharePoint” which allows to construct a custom request.

Essentially the process will involve the normal OOTB SharePoint integration with Dynamics 365 and will have the following steps:

  • Get GUID of current record through the form integration data
  • Query for the relative URL stored on the document location record related to the primary record (using Dynamics 365 connector, as at time CDS didn’t have document locations)
  • Construct an HTTP request to SharePoint to retrieve the list of documents
  • Return the Array of documents to the PowerApp

I’m using an Embedded Canvas App which is on a Dynamics 365 form. This will take the GUID of the record and pass this to the Flow to retrieve the SharePoint Documents. Below is the completed Canvas App on the form.

Let’s Begin

I’m going to start with the call from PowerApps to Flow and then look at how to get the list of documents from SharePoint.

From the PowerApp I have setup a timer to end after a time interval. This call the Flow in question and pass the GUID. I’m using the form integration data to get the contact GUID and passing that to Flow.

The GUID being passed is used to query back into Dynamics to retrieve the SharePoint URL. This is done using a PowerApp parameter to pass to the GUID to the filter query of the Dynamics 365 Connector.

The above query will return a list of records so I filter out to get only the first. I use the relative URL to build the API call to SharePoint. If you do a google search you can learn more regarding the SharePoint API. Essentially I’m calling a GET to the GetFolderByServerRelativeURL function to return back an array of files in the folder.

This is where it gets a little interesting as you can see below I hard code the contact folder and pass the relative URL retrieved from the document location query using body(‘Get_Loc’)[‘value’][0]?[‘relativeurl’] as parameters. Going forward this would need to be configurable to each entity type.

The API call will return an array of documents to be passed back to the PowerApp using a Response action. Worth performing a test so a sample can be used to generate a schema.

Back in the PowerApp designer it is simply a matter of binding the Gallery to the collection retrieved back from Flow. I have hard coded the SharePoint URL so think about providing a configuration for this.

The Edit button is to implement a way to edit document meta data which I will blog about soon.

Implementing all the above the Embedded Canvas App can now pull in the SharePoint documents onto the form.

Check out an old post on Embedded Canvas Apps: https://the365hero.com/blog/2018/12/24/embedded-canvas-apps-for-dynamics-365/

  1. uday v

    Hello 365hero,

    Were you able to retrieve the lookup value from the contact in the embedded canvas app?
    Like the GUID of the parentcustomerid of the contact?

    Thanks
    Uday

    • The 365 Hero

      Hi Mate,

      From what I can see you might need to do a Lookup formula to the account and contact entities with the guid from the Customer lookup.

      Try this:

      If(ThisItem.’Customer Type’ = “accounts”, LookUp(Accounts, accountid = GUID(ThisItem.Customer), ‘Account’), LookUp(Contacts, contactid = GUID(ThisItem.Customer), ‘Contact’))

      Cheers,

      Josh

  2. Jason

    Noticed the (+) symbol at the top of your app and wondered if you have been able to implement adding documents using the (+) through your canvas app?

    • The 365 Hero

      Hi Jason,

      Late reply but yes that was on my radar to implement. I also experimented with document meta data as well. This is always a requirement to be able to tag documents.

  3. Triasha

    I have followed every step that is mentioned above, but I am not able to get the documents in the collection.
    This is the formula which I have used to call the Ms flow:

    ClearCollect(AllDocuments, ‘Retrievedocumentforeachrecord.’.Run(BrowseGalleryObjectives.Selected.Objective))

    And also I am not able to bind the gallery as I am not getting the ServerRelativeUrl.

    • The 365 Hero

      Hi Triasha,

      Sounds to me like you need to specify a sample payload to generate the schema to enable the binding in the Response action back to the PowerApp. If you run a test and get the list of documents from SharePoint you can use the output to generate the Schema. Here is the response body JSON Schema in my flow. Hope it helps.

      {
      “type”: “array”,
      “items”: {
      “type”: “object”,
      “properties”: {
      “__metadata”: {
      “type”: “object”,
      “properties”: {
      “id”: {
      “type”: “string”
      },
      “uri”: {
      “type”: “string”
      },
      “type”: {
      “type”: “string”
      }
      }
      },
      “Author”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “CheckedOutByUser”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “EffectiveInformationRightsManagementSettings”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “InformationRightsManagementSettings”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “ListItemAllFields”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “LockedByUser”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “ModifiedBy”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “Properties”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “VersionEvents”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “Versions”: {
      “type”: “object”,
      “properties”: {
      “__deferred”: {
      “type”: “object”,
      “properties”: {
      “uri”: {
      “type”: “string”
      }
      }
      }
      }
      },
      “CheckInComment”: {
      “type”: “string”
      },
      “CheckOutType”: {
      “type”: “integer”
      },
      “ContentTag”: {
      “type”: “string”
      },
      “CustomizedPageStatus”: {
      “type”: “integer”
      },
      “ETag”: {
      “type”: “string”
      },
      “Exists”: {
      “type”: “boolean”
      },
      “IrmEnabled”: {
      “type”: “boolean”
      },
      “Length”: {
      “type”: “string”
      },
      “Level”: {
      “type”: “integer”
      },
      “LinkingUri”: {
      “type”: “string”
      },
      “LinkingUrl”: {
      “type”: “string”
      },
      “MajorVersion”: {
      “type”: “integer”
      },
      “MinorVersion”: {
      “type”: “integer”
      },
      “Name”: {
      “type”: “string”
      },
      “ServerRelativeUrl”: {
      “type”: “string”
      },
      “TimeCreated”: {
      “type”: “string”
      },
      “TimeLastModified”: {
      “type”: “string”
      },
      “Title”: {
      “type”: “string”
      },
      “UIVersion”: {
      “type”: “integer”
      },
      “UIVersionLabel”: {
      “type”: “string”
      },
      “UniqueId”: {
      “type”: “string”
      }
      },
      “required”: [
      “__metadata”,
      “Author”,
      “CheckedOutByUser”,
      “EffectiveInformationRightsManagementSettings”,
      “InformationRightsManagementSettings”,
      “ListItemAllFields”,
      “LockedByUser”,
      “ModifiedBy”,
      “Properties”,
      “VersionEvents”,
      “Versions”,
      “CheckInComment”,
      “CheckOutType”,
      “ContentTag”,
      “CustomizedPageStatus”,
      “ETag”,
      “Exists”,
      “IrmEnabled”,
      “Length”,
      “Level”,
      “LinkingUri”,
      “LinkingUrl”,
      “MajorVersion”,
      “MinorVersion”,
      “Name”,
      “ServerRelativeUrl”,
      “TimeCreated”,
      “TimeLastModified”,
      “Title”,
      “UIVersion”,
      “UIVersionLabel”,
      “UniqueId”
      ]
      }
      }

      • Triasha

        This is the schema which I have used

        {
        “type”: “object”,
        “properties”: {
        “d”: {
        “type”: “object”,
        “properties”: {
        “results”: {
        “type”: “array”,
        “items”: {
        “type”: “object”,
        “properties”: {
        “__metadata”: {
        “type”: “object”,
        “properties”: {
        “id”: {
        “type”: “string”
        },
        “uri”: {
        “type”: “string”
        },
        “type”: {
        “type”: “string”
        }
        }
        },
        “Author”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “CheckedOutByUser”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “EffectiveInformationRightsManagementSettings”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “InformationRightsManagementSettings”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “ListItemAllFields”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “LockedByUser”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “ModifiedBy”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “Properties”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “VersionEvents”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “Versions”: {
        “type”: “object”,
        “properties”: {
        “__deferred”: {
        “type”: “object”,
        “properties”: {
        “uri”: {
        “type”: “string”
        }
        }
        }
        }
        },
        “CheckInComment”: {
        “type”: “string”
        },
        “CheckOutType”: {
        “type”: “integer”
        },
        “ContentTag”: {
        “type”: “string”
        },
        “CustomizedPageStatus”: {
        “type”: “integer”
        },
        “ETag”: {
        “type”: “string”
        },
        “Exists”: {
        “type”: “boolean”
        },
        “IrmEnabled”: {
        “type”: “boolean”
        },
        “Length”: {
        “type”: “string”
        },
        “Level”: {
        “type”: “integer”
        },
        “LinkingUri”: {},
        “LinkingUrl”: {
        “type”: “string”
        },
        “MajorVersion”: {
        “type”: “integer”
        },
        “MinorVersion”: {
        “type”: “integer”
        },
        “Name”: {
        “type”: “string”
        },
        “ServerRelativeUrl”: {
        “type”: “string”
        },
        “TimeCreated”: {
        “type”: “string”
        },
        “TimeLastModified”: {
        “type”: “string”
        },
        “Title”: {},
        “UIVersion”: {
        “type”: “integer”
        },
        “UIVersionLabel”: {
        “type”: “string”
        },
        “UniqueId”: {
        “type”: “string”
        }
        },
        “required”: [
        “__metadata”,
        “Author”,
        “CheckedOutByUser”,
        “EffectiveInformationRightsManagementSettings”,
        “InformationRightsManagementSettings”,
        “ListItemAllFields”,
        “LockedByUser”,
        “ModifiedBy”,
        “Properties”,
        “VersionEvents”,
        “Versions”,
        “CheckInComment”,
        “CheckOutType”,
        “ContentTag”,
        “CustomizedPageStatus”,
        “ETag”,
        “Exists”,
        “IrmEnabled”,
        “Length”,
        “Level”,
        “LinkingUri”,
        “LinkingUrl”,
        “MajorVersion”,
        “MinorVersion”,
        “Name”,
        “ServerRelativeUrl”,
        “TimeCreated”,
        “TimeLastModified”,
        “Title”,
        “UIVersion”,
        “UIVersionLabel”,
        “UniqueId”
        ]
        }
        }
        }
        }
        }
        }

        • The 365 Hero

          Have you tried deleting the flow connection from the PowerApp and adding again?

          • Triasha

            Thank you for your help. It is working after I deleted the flow connection.

  4. Ozzie

    Hi, this is a really interesting topic and I was very excited to test this functionality.
    I have created the app, the collection, setup integration with sharepoint on my trial, I created the flow from the app, etc. All seems to be working when I look at the flow history, including the inputs an outputs (which proves that Flow is triggered with right parameters). However, I do not see any data returned to my collection.
    The output from the “Answer” step seems to be correct and according to the schemas that you and Trisha discussed, but still I get nothing returned.
    Am I missing something? One thing I noticed different from your syntax is that on my trial (in swedish) it is using following syntax:

    ClearCollect(Samling1;’PowerApp->SvarapåenPowerAppellerettflöde,Villkor,Listposter…_8′.Run())

    A kind of strange name for the Flow, i agree, but nevertheless, it seems to be triggered.

    Thanks in advance!
    Ozzie

    • The 365 Hero

      Hallå

      Without knowing too much on your flow, I would be expecting you to pass the GUID of the record over. Something like this ClearCollect(Samling1;’PowerApp->SvarapåenPowerAppellerettflöde,Villkor,Listposter…_8′.Run(GUID of Record))

  5. Ozzie

    Hi again, in the Run methond, I have my recordid, for some reason it was not displayed here. Actually it takes the content of @ModelDrivenFormIntegration, but for my test purposes I chose a record.

    ‘PowerApp->SvarapåenPowerAppellerettflöde,Villkor,Listposter…_8’.Run(recordId)

    Thanks again

    • The 365 Hero

      I’m assuming you have the response action all set up with the schema. You might need to delete the Flow connection from the PowerApp and re-add. The PowerApp should read the schema and setup everything for the binding to the collection.

  6. Ozzie

    Hi thanks for your answer. Removing and adding the Flow helps to a certain extend.
    I get an empty Collection with the columns like Name, url, etc. Teh Flow historey indicates values returned.
    I suspect there is a difference in teh Sharepoint step. I see your answer uses d.result from Sharepoint. I do not have that available, in only have “Body” returning from Sharepoint, whose icon also looks different. Maybe updated since this was published?

    Thank you!

  7. Ozzie

    Hi again,

    I finally managed to get my data in the collection. Thank you so much! This is great!
    Lots of new possitbilities 🙂

    BR
    Ozzie

    • The 365 Hero

      So many possibilities. I also got working the ability to update document metadata from the PowerApp as well 🙂

  8. Bhaskar

    Hi, this is a great article but do you know if we can do this with a custom entity? I am having trouble even embedding the canvas app on a custom entity form. Have you tried this with a custom entity that does not support showing the documents area in the main form?

    • The 365 Hero

      Hey, yes embedding should work on custom entity. Are you using UCI?

      • Bhaskar

        Thank you for your prompt response.
        Yes, I am using UCI. When I click on the “Customize” button on the Canvas App control in the form I am redirected to a canvas app but the canvas app has a form and not a gallery . So I added a gallery, and set the datasource of the gallery to filtered sharepoint DS with the matching condition set to find the name of the CRM record to be a part of the full path of the file. I can see the file populating correctly and I have a next button set for each record returned. But I cannot figure out how to open the word file in a browser directly. If I use the full path, it downloads the file but does not let me edit it.
        Not sure what I am missing here.

        • Bhaskar

          Nevermind, I figured out how to do it. Launch(ThisItem.’Document ID’) does the trick. 🙂

  9. Ozzie

    Hi!

    one thing I noticed is that I get files returned, but not folders in the returned collection. Would you happen to know why?

    • The 365 Hero

      Yes the SharePoint call is for files only. Check out the SharePoint API to return folders something like: getFolderByServerRelativeUrl

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.