October 29, 2018

Show a list like BIM 360 Docs Plans folder

BIM 360 Plans folder shows extracted views of a file, how can we mimic that via API? 

First we have to start looking at the visibleType property, which defines what's visible on a given folder. On Plans folder that will be Document (and not Files). Then as we have 2 main attributes, we need to check both data for what's visible, then included to get views names, then back to data to find where it is visible (URN). The following C# sample is based on the Learn Forge tutorial, but it's modified as shown below (also full sample here).

private async Task<IList<jsTreeNode>> GetFolderContents(string href)
    IList<jsTreeNode> nodes = new List<jsTreeNode>();

    // the API SDK
    FoldersApi folderApi = new FoldersApi();
    folderApi.Configuration.AccessToken = Credentials.TokenInternal;

    // extract the projectId & folderId from the href
    string[] idParams = href.Split('/');
    string folderId = idParams[idParams.Length - 1];
    string projectId = idParams[idParams.Length - 3];

    // check if folder specifies visible types
    JArray visibleTypes = null;
    dynamic folder = (await folderApi.GetFolderAsync(projectId, folderId)).ToJson();
    if (folder.data.attributes != null && folder.data.attributes.extension != null && folder.data.attributes.extension.data != null && !(folder.data.attributes.extension.data is JArray) && folder.data.attributes.extension.data.visibleTypes != null)
        visibleTypes = folder.data.attributes.extension.data.visibleTypes;

    var folderContents = await folderApi.GetFolderContentsAsync(projectId, folderId);
    // the GET Folder Contents has 2 main properties: data & included (not always available)
    var folderData = new DynamicDictionaryItems(folderContents.data);
    var folderIncluded = (folderContents.Dictionary.ContainsKey("included") ? new DynamicDictionaryItems(folderContents.included) : null);

    // let's start iterating the FOLDER DATA
    foreach (KeyValuePair<string, dynamic> folderContentItem in folderData)
        // do we need to skip some items? based on the visibleTypes of this folder
        string extension = folderContentItem.Value.attributes.extension.type;
        if (extension.IndexOf("Folder") /*any folder*/ == -1 && visibleTypes != null && !visibleTypes.ToString().Contains(extension)) continue;

        // if the type is items:autodesk.bim360:Document we need some manipulation...
        if (extension.Equals("items:autodesk.bim360:Document"))
            // as this is a DOCUMENT, lets interate the FOLDER INCLUDED to get the name (known issue)
            foreach (KeyValuePair<string, dynamic> includedItem in folderIncluded)
                // check if the id match...
                if (includedItem.Value.relationships.item.data.id.IndexOf(folderContentItem.Value.id) != -1)
                    // found it! now we need to go back on the FOLDER DATA to get the respective FILE for this DOCUMENT
                    foreach (KeyValuePair<string, dynamic> folderContentItem1 in folderData)
                        if (folderContentItem1.Value.attributes.extension.type.IndexOf("File") == -1) continue; // skip if type is NOT File

                        // check if the sourceFileName match...
                        if (folderContentItem1.Value.attributes.extension.data.sourceFileName == includedItem.Value.attributes.extension.data.sourceFileName)
                            // ready!

                            // let's return for the jsTree with a special id:
                            // itemUrn|versionUrn|viewableId
                            // itemUrn: used as target_urn to get document issues
                            // versionUrn: used to launch the Viewer
                            // viewableId: which viewable should be loaded on the Viewer
                            // this information will be extracted when the user click on the tree node, see ForgeTree.js:136 (activate_node.jstree event handler)
                            string treeId = string.Format("{0}|{1}|{2}",
                                folderContentItem.Value.id, // item urn
                                Base64Encode(folderContentItem1.Value.relationships.tip.data.id), // version urn
                                includedItem.Value.attributes.extension.data.viewableId // viewableID
                            nodes.Add(new jsTreeNode(treeId, WebUtility.UrlDecode(includedItem.Value.attributes.name), "bim360documents", false));
            // non-Plans folder items
            nodes.Add(new jsTreeNode(folderContentItem.Value.links.self.href, folderContentItem.Value.attributes.displayName, (string)folderContentItem.Value.type, true));

    return nodes;

Finally, as in most of our samples we show the 1st viewable, we need a small change to show a specific view instead (if that's the case). The following code is based on the Basic Application tutorial and we just need to modify the following (full sample here):

      if (viewableId !== undefined) {
        viewables.forEach(function (viewable) {
          if (viewable.data.viewableID == viewableId)
            viewerApp.selectItem(viewable.data, onItemLoadSuccess, onItemLoadFail);
        viewerApp.selectItem(viewables[0].data, onItemLoadSuccess, onItemLoadFail); // this is the original line


Related Posts

March 25, 2019

Mini-map with Geolocation extension

Using the built-in Autodesk.Geolocation extension to implement a minimap

Read More

March 22, 2019

Adding custom properties to Property panel

Minimum code to add custom data to the Viewer Properties panel

Read More