/////////////////////////////////////////////////////////////////////
// Copyright (c) Autodesk, Inc. All rights reserved
// Written by Forge Partner Development
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
/////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Autodesk.Forge;
using Autodesk.Forge.Model;
using Newtonsoft.Json.Linq;
using System.Net;
namespace forgeSample.Controllers
{
public class DataManagementController : ControllerBase
{
///
/// Credentials on this request
///
private Credentials Credentials { get; set; }
///
/// GET TreeNode passing the ID
///
[HttpGet]
[Route("api/forge/datamanagement")]
public async Task> GetTreeNodeAsync(string id)
{
Credentials = await Credentials.FromSessionAsync(base.Request.Cookies, Response.Cookies);
if (Credentials == null) { return null; }
IList nodes = new List();
if (id == "#") // root
return await GetHubsAsync();
else
{
string[] idParams = id.Split('/');
string resource = idParams[idParams.Length - 2];
switch (resource)
{
case "hubs": // hubs node selected/expanded, show projects
return await GetProjectsAsync(id);
case "projects": // projects node selected/expanded, show root folder contents
return await GetProjectContents(id);
case "folders": // folders node selected/expanded, show folder contents
return await GetFolderContents(id);
case "items":
return await GetItemVersions(id);
}
}
return nodes;
}
private async Task> GetHubsAsync()
{
IList nodes = new List();
// the API SDK
HubsApi hubsApi = new HubsApi();
hubsApi.Configuration.AccessToken = Credentials.TokenInternal;
var hubs = await hubsApi.GetHubsAsync();
foreach (KeyValuePair hubInfo in new DynamicDictionaryItems(hubs.data))
{
// check the type of the hub to show an icon
string nodeType = "hubs";
switch ((string)hubInfo.Value.attributes.extension.type)
{
case "hubs:autodesk.core:Hub":
nodeType = "hubs"; // if showing only BIM 360, mark this as 'unsupported'
break;
case "hubs:autodesk.a360:PersonalHub":
nodeType = "personalHub"; // if showing only BIM 360, mark this as 'unsupported'
break;
case "hubs:autodesk.bim360:Account":
nodeType = "bim360Hubs";
break;
}
// create a treenode with the values
jsTreeNode hubNode = new jsTreeNode(hubInfo.Value.links.self.href, hubInfo.Value.attributes.name, nodeType, !(nodeType == "unsupported"));
nodes.Add(hubNode);
}
return nodes;
}
private async Task> GetProjectsAsync(string href)
{
IList nodes = new List();
// the API SDK
ProjectsApi projectsApi = new ProjectsApi();
projectsApi.Configuration.AccessToken = Credentials.TokenInternal;
// extract the hubId from the href
string[] idParams = href.Split('/');
string hubId = idParams[idParams.Length - 1];
var projects = await projectsApi.GetHubProjectsAsync(hubId);
foreach (KeyValuePair projectInfo in new DynamicDictionaryItems(projects.data))
{
// check the type of the project to show an icon
string nodeType = "projects";
switch ((string)projectInfo.Value.attributes.extension.type)
{
case "projects:autodesk.core:Project":
nodeType = "a360projects";
break;
case "projects:autodesk.bim360:Project":
nodeType = "bim360projects";
break;
}
// create a treenode with the values
jsTreeNode projectNode = new jsTreeNode(projectInfo.Value.links.self.href, projectInfo.Value.attributes.name, nodeType, true);
nodes.Add(projectNode);
}
return nodes;
}
private async Task> GetProjectContents(string href)
{
IList nodes = new List();
// the API SDK
ProjectsApi projectApi = new ProjectsApi();
projectApi.Configuration.AccessToken = Credentials.TokenInternal;
// extract the hubId & projectId from the href
string[] idParams = href.Split('/');
string hubId = idParams[idParams.Length - 3];
string projectId = idParams[idParams.Length - 1];
var project = await projectApi.GetProjectAsync(hubId, projectId);
var rootFolderHref = project.data.relationships.rootFolder.meta.link.href;
return await GetFolderContents(rootFolderHref);
}
private async Task> GetFolderContents(string href)
{
IList nodes = new List();
// 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 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 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 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));
}
}
}
}
}
else
{
// non-Plans folder items
nodes.Add(new jsTreeNode(folderContentItem.Value.links.self.href, folderContentItem.Value.attributes.displayName, (string)folderContentItem.Value.type, true));
}
}
return nodes;
}
private string GetName(DynamicDictionaryItems folderIncluded, KeyValuePair folderContentItem)
{
return "N/A";
}
private async Task> GetItemVersions(string href)
{
IList nodes = new List();
// the API SDK
ItemsApi itemApi = new ItemsApi();
itemApi.Configuration.AccessToken = Credentials.TokenInternal;
// extract the projectId & itemId from the href
string[] idParams = href.Split('/');
string itemId = idParams[idParams.Length - 1];
string projectId = idParams[idParams.Length - 3];
var versions = await itemApi.GetItemVersionsAsync(projectId, itemId);
foreach (KeyValuePair version in new DynamicDictionaryItems(versions.data))
{
DateTime versionDate = version.Value.attributes.lastModifiedTime;
string verNum = version.Value.id.Split("=")[1];
string userName = version.Value.attributes.lastModifiedUserName;
string urn = string.Empty;
try { urn = (string)version.Value.relationships.derivatives.data.id; }
catch { urn = Base64Encode(version.Value.id); } // some BIM 360 versions don't have viewable
jsTreeNode node = new jsTreeNode(
urn,
string.Format("v{0}: {1} by {2}", verNum, versionDate.ToString("dd/MM/yy HH:mm:ss"), userName),
"versions",
false);
nodes.Add(node);
}
return nodes;
}
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes).Replace("/", "_");
}
public class jsTreeNode
{
public jsTreeNode(string id, string text, string type, bool children)
{
this.id = id;
this.text = text;
this.type = type;
this.children = children;
}
public string id { get; set; }
public string text { get; set; }
public string type { get; set; }
public bool children { get; set; }
}
}
}