powerVault – Vault API made simple

2015-04-03_10-06-39

The first time we blogged about PowerShell was back in March 2012 (3 years ago). It was unknown in the Autodesk Data Management scene. Meanwhile it’s become quite popular.

With Vault 2015 R2 we took the next step for making the Vault API even more accessible. We created powerVault!

It’s a native PowerShell extension that allows you to talk with Vault via pure PowerShell command lets. With powerVault, you get command lets for dealing with files and items. For instance Open-VaultConnection for establishing a connection to Vault, or Get-VaultFile for getting/downloading a file, or Get-VaultItem for getting an item. But also Add-VaultFile or Update-VaultFile or Update-VaultItem. Such command lets make it very easy to either get information from Vault or creating and updating information in Vault. You don’t have to deal with the complexity of the Vault API, instead you can just execute the command you like and let the command let do the rest for you. As an example:

Import-Module powerVault

Open-VaultConnection -Server localhost -Vault Vault -User "Administrator" -Password ""

$CatchAssembly = Add-VaultFile -From "C:\Users\Publ... ...semblies\Catch Assembly.iam" -To "$/Designs/Assemblies/PadLock/Catch Assembly.iam" -FileClassification "Inventor" -Comment "Imported via powerVault"$
CatchAssembly = Update-VaultFile -File $CatchAssembly.'Full Path' -Properties @{"Title"="Catch Assembly"; "Company"="coolOrange"} -Category "Engineering"

$ComboAssembly = Add-VaultFile -From "C:\Users\Publ... ...ssemblies\Combo Assembly.iam" -To "$/Designs/Assemblies/PadLock/Combo Assembly.iam" -FileClassification "Inventor" -Comment "Imported via powerVault"
$ComboAssembly = Update-VaultFile -File $ComboAssembly.'Full Path' -Properties @{"Title"="Combo Assembly"; "Company"="coolOrange"} -Category "Engineering"

$padLock = Add-VaultFile -From "C:\Users\Publ... ...ssemblies\Pad Lock.iam" -To "$/Designs/Assemblies/PadLock/Pad Lock.iam" -FileClassification "Inventor" -Comment "Imported via powerVault"
$padLock = Update-VaultFile -File $padLock.'Full Path' -Properties @{"Title"="Pad Lock"; "Company"="coolOrange"} -Category "Engineering" -AddChilds @($CatchAssembly.'Full Path',$ComboAssembly.'Full Path')

The first line imports the powerVault into your PowerShell session (Import-Module powerVault). With the next few lines we connect to Vault (Open-VaultConnection), we add 3 files to Vault (Add-VaultFile) and then we update those files with properties and set the category. The Add-VaultFile command let creates the folder in Vault, if not existing, and handles check-out/-in in case a file already exists. The last Update-VaultFile also sets the references between the main assembly and the two sub assemblies. And why not even release the files:

$CatchAssembly = Update-VaultFile -File $CatchAssembly.'Full Path' -Status "Released"
$ComboAssembly = Update-VaultFile -File $ComboAssembly.'Full Path' -Status "Released"
$padLock = Update-VaultFile -File $padLock.'Full Path' -Status "Released"

As you can see, with single lines of codes and some readable options, you can import and update files into Vault. In a similar way you can get a file object with Get-VaultFile like this

$file = Get-VaultFile -File "$/Designs/Assemblies/PadLock/Pad Lock.iam"

You will notice that the $file object exposes already all the file properties, so you can access the title just by $file.Title, or to the $file.’Engr Approved By’ or $file.’File Name (Historical)’. Yes, the property names are exactly as exposed in Vault, including blank spaces and brackets. In such cases the property name must be set in quotes (‘…’).

Now, let’s download the assembly into a local folder

Get-VaultFile -File "$/Designs/Assemblies/PadLock/Pad Lock.iam" -DownloadPath c:\temp\padlock

You will notice that the assembly including children will be downloaded and the references will be redirected, so it’s ready to be opened!

And what if you like to get all files of a given folder?

$files = Get-VaultFiles -Folder "$/Designs/Assemblies/PadLock"

Or why not making a search in Vault for all the released drawings?

$files = Get-VaultFiles -Properties @{"File Extension"="idw";"State"="Released"}

Meanwhile you should get a feel for the simplicity of powerVault. For those of you who like to use the native API, no problem, as by establishing the connection to Vault (Open-VaultConnection), you get also the variables $vault (complete webservices), $vaultConnection (VDF) and $vaultExplorerUtil. And you are free to mix the command lets with the native Vault API. So, you get the best of both.

There are more command lets and even more will come. The current documentation can be found here http://wiki.coolorange.com/display/powerjobs/Cmdlets, however with 2016 versions (few weeks) we will create a dedicated area for powerVault.

powerVault is part of powerJobs and powerGate, so you can imagine how easy it is to write jobs with powerJobs or customize powerGate. And as powerVault is now a stand alone and free app, you can use it within your Data Standard projects or for stand alone scripts.

You can now download powerVault from here http://www.coolorange.com/en/download.php?product=powerVault

With the 2016 Version, a dedicated web page will come together with the wiki page. But as we are so excited about powerVault, we were not willing to hold this cool thing back for longer.

There is another thing important to be mentioned. Compatibility! As the syntax of powerVault does not change, the scripts you make with 2015 R2, will also work with 2016 and later versions. We will release an according powerVault version for each Vault version, so that you don’t have to care about upgrading your code.

Curious enough?? Get started by downloading powerVault, or if you already have powerJobs or powerGate just start a PowerShell. Have a look into your installed applications, as maybe powerVault is already there.

2015-04-02_08-58-30

Just one remark. As powerVault is built with latest technology (.Net 4), PowerShell on Windows 7 machine still runs with .Net 2 and does not load powerVault in the first place. However, if you start a PowerShell and execute the following lines, the supported .Net framework will be set to latest version.

add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1
reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

Restart the PowerShell and you are ready to go.

Have fun!!

Posted in Data Standard, powerGate, powerJobs, PowerShell, Vault API | 1 Comment

New file inherits from template

2015-03-27_15-31-52

Recently, we received an interesting question about Data Standard. In Vault, it’s possible to create new files based on templates. The question was whether it’s possible to inherit the category and maybe also some properties from the template file into the new file dialog. Obviously, the answer is YES!

The first thing we have to do is to hook the SelectionChanged event from the template combobox. In this case, the combobox is bound to a fixed Data Standard property called SelectedTemplate, which is not accessible through the Prop array. So, we will hook directly the selection changed event from the combobox, called TemplateCB. We do this inside the InitializeWindow function, like this:

function InitializeWindow
{
 ..
 .....

 $dsWindow.FindName("TemplateCB").add_SelectionChanged({
  OnTemplateChanged
 })
}

Every time, the user selects a template, our OnTemplateChanged will be fired. Inside such function, we will take the file information from the selected template file and assign it to our dialog.

As the selected template path and template name are sort of internal properties, we will retrieve these information directly from the DataContext of our dialog.

$context = $dsWindow.DataContext
$path = $context.TemplatePath
$file = $context.SelectedTemplate

Now that we know which template the user has selected from which folder, via the Vault API we can get the file information. The following functions can be placed either in a separate PS1 file or somewhere in the default.ps1.

function OnTemplateChanged
{
 $context = $dsWindow.DataContext
 $path = $context.TemplatePath
 $file = $context.SelectedTemplate
 $template = $path + "/" + $file
 $folder = $vault.DocumentService.GetFolderByPath($path)
 $files = $vault.DocumentService.GetLatestFilesByFolderId($folder.Id,$false)
 $file = $files | Where-Object { $_.Name -eq $file }
 $Prop["_Category"].Value = $file.Cat.CatName

 $properties = GetFileProperties -fileId $file.Id
 $Prop["Title"].Value = $properties["Title"]
}

At the end we can set the current category with the category of the template file. In the last two lines we set also the title with the same value as the template, so in case your templates have some default values, they can be brought over to the new file.

Now, as the file object does not expose the properties, we need to pick them via the Vault API. For this purpose, I created another small function that retrieves the properties and returns an array with property name and value.

function GetFileProperties($fileId)
{
 $global:propDefs = $vault.PropertyService.GetPropertyDefinitionsByEntityClassId("FILE")
 $props = $vault.PropertyService.GetPropertiesByEntityIds("FILE",@($fileId))
 $properties = @{}
 foreach ($prop in $props) {
  $propDef = $global:propDefs | Where-Object { $_.Id -eq $prop.PropDefId }
  $properties[$propDef.DispName] = $Prop.Val
 }
 return $properties
}

You will notice that when you select a template now, the dialog takes a second or so to update. The reason is that collecting all the properties from the template is time consuming and delays the dialog refresh. If you comments the two lines

#$properties = GetFileProperties -fileId $file.Id
#$Prop["Title"].Value = $properties["Title"]

You will notice that the dialog is again reactive.

So, now it’s up to you to define which information shall be copied from the template into your new file.

Have fun!

Posted in Data Standard | Leave a comment

Data Standard Quick Reference

2015-03-18_09-11-23

During the trainings we held on Data Standard and the conversations we had with you, we realized that Data Standard could be actually summarized into a single page. And so we did it. The result is this document.

Data Standard Quick Reference 2015R2

On the first page, you will find a summary of the most used WPF controls, such as Textbox, ComboBox, etc. Sure, there are more controls and also for the described controls, there are more properties, but if you know/use these, you are good to go!

On the second page, you will find a short description for the Inventor and AutoCAD configuration file and a description for the PowerShell functions.

The document is not a “getting started”, or tutorial or a how-to. It’s a quick reference. It’s a document you could print and leave on your desk, and pick it up whenever you can’t remember the syntax. The content is extracted from the Autodesk online help, where it is described in a broader way. This document aims to summarize all the things you must know, or all the things you usually run into while dealing with Data Standard.

Finally, it also shows that Data Standard can be summarized on one page (front-rear), which says a lot!!

So, we hope you will enjoy this document, and as usual, if you have comments or suggestions, just get back to us.

Posted in Data Standard | 3 Comments

View And Data API with PowerShell

Last week, we posted about the View And Data API and showed how to upload an assembly from Vault via powerJobs and PowerShell. This week, I’d like to add some more background. For this purpose I’ve created a stand-alone script that uploads a little sample assembly.

In order to get started, you need your own access keys. It’s free, so go on https://developer.autodesk.com and register for a free account. Under “My Apps” you can create a new app. Just give it a name and select the View and Data API. Once you have your app registered, you will receive a Consumer Key and Consumer Secret. These two strings are necessary for the login. In the script, at the top, you’ll find the two variables where you can enter your keys.

The following script will take an assembly from the Inventor sample folder and upload it to the cloud. As this is pure PowerShell, you may create your own batch scripts for uploading the files. Here is the complete script:

<br />
cls<br />
$ConsumerKey=&quot;&lt;youConsumerKeyHere&gt;&quot;<br />
$ConsumerSecret=&quot;&lt;ourConsumerSecretHere&gt;&quot;</p>
<p>$samplePath = &quot;C:\Users\Public\Documents\Autodesk\Inventor 2015\Samples\Models\Assemblies\Tuner&quot;</p>
<p>#login<br />
$login = Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/authentication/v1/authenticate&quot; -ContentType &quot;application/x-www-form-urlencoded&quot; -Method Post -Body &quot;client_id=$ConsumerKey&amp;client_secret=$ConsumerSecret&amp;grant_type=client_credentials&quot;<br />
$auth = $login.token_type + &quot; &quot; + $login.access_token</p>
<p>#create bucket<br />
$bucketName = &quot;tuner&quot; #must be lower case and/or numbers<br />
$policy = &quot;transient&quot; #transient</p>
<p>$json = '{&quot;bucketKey&quot;:&quot;'+$bucketName+'&quot;,&quot;policy&quot;:&quot;'+$policy+'&quot;}'<br />
$bucket = Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/oss/v1/buckets&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}</p>
<p>#upload assembly<br />
$assembly = Get-Item &quot;$samplePath\Tuner.iam&quot;<br />
$uploadedFiles = @()<br />
$uploadedFiles += Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/oss/v1/buckets/$bucketName/objects/$($assembly.Name.ToLower())&quot; -ContentType &quot;application/octet-stream&quot; -Method Put -Headers @{&quot;Authorization&quot;=$auth;&quot;Content-Length&quot;=$assembly.Length;&quot;Expect&quot;=&quot;&quot;} -InFile $assembly.FullName.ToLower()</p>
<p>$json = '{ &quot;master&quot; : &quot;urn:adsk.objects:os.object:'+$bucketName+'/'+$assembly.Name.ToLower()+'&quot;, &quot;dependencies&quot; : ['</p>
<p>#upload children<br />
$files = Get-ChildItem -LiteralPath &quot;$samplePath\Tuner Components&quot; -File<br />
foreach ($file in $files) {<br />
  echo &quot;uploading $($file.Name) ...&quot;<br />
  $uploadedFiles += Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/oss/v1/buckets/$bucketName/objects/$($file.Name)&quot; -ContentType &quot;application/octet-stream&quot; -Method Put -Headers @{&quot;Authorization&quot;=$auth;&quot;Content-Length&quot;=$file.Length;&quot;Expect&quot;=&quot;&quot;} -InFile $file.FullName<br />
  $json += '{ &quot;file&quot; : &quot;urn:adsk.objects:os.object:'+$bucketName+'/'+$file.Name+'&quot;, &quot;metadata&quot; : { &quot;childPath&quot; : &quot;'+$file.Name+'&quot;, &quot;parentPath&quot; : &quot;'+$assembly.Name+'&quot; } },'<br />
}</p>
<p>$json = $json.Substring(0,$json.Length-1)<br />
$json += ']}'<br />
#set references<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/references/v1/setreference&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}</p>
<p>#register view service<br />
foreach ($uploadedFile in $uploadedFiles) {<br />
  $base64 = [convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($uploadedFile.objects[0].id))<br />
  $json = '{&quot;urn&quot;:&quot;'+$base64+'&quot;}'<br />
  $result = Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/viewingservice/v1/register&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}<br />
  echo &quot;$($uploadedFile.objects[0].key) = $($result.Result)&quot;<br />
}</p>
<p>#get status<br />
foreach ($uploadedFile in $uploadedFiles) {<br />
  $base64 = [convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($uploadedFile.objects[0].id))<br />
  $status.progress = &quot;&quot;<br />
  while ($status.progress -ne &quot;complete&quot;) {<br />
    $status = Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/viewingservice/v1/$base64/status&quot; -ContentType &quot;application/json&quot; -Method Get -Headers @{&quot;Authorization&quot;=$auth}<br />
    Start-Sleep -Seconds 1<br />
  }<br />
  echo &quot;$($uploadedFile.objects[0].key) = $($status.progress)&quot;<br />
}</p>
<p>echo &quot;Access token: $($login.access_token)&quot;<br />
$file = $uploadedFiles[1].objects[0]<br />
$base64 = [convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($file.id))<br />
echo &quot;$($file.key) urn: $base64&quot;<br />

As mentioned in my previous post, you’ll need PowerShell 3.0 (default on Windows 8), as it provides the Invoke-RestMethod.

The first step is to login into the view and data API backend. We do this with this command-let

<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/authentication/v1/authenticate&quot; -ContentType &quot;application/x-www-form-urlencoded&quot; -Method Post -Body &quot;client_id=$ConsumerKey&amp;client_secret=$ConsumerSecret&amp;grant_type=client_credentials&quot;<br />

Then we create a folder (bucket) where we can upload all our files, like this

<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/oss/v1/buckets&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}<br />

Unfortunately, there is no way (yet) to check whether the bucket is already created or not, so the first time we execute the command above, everything should be fine, but the next time the commandlets returns an error

<br />
Invoke-RestMethod : {&quot;reason&quot;:&quot;Bucket already exists&quot;}<br />

The script will continue anyway, but the error message is annoying. Hopefully there will be a call for testing a bucket existence or retrieve the list of buckets soon.

Then, we upload the assembly

<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/oss/v1/buckets/$bucketName/objects/$($assembly.Name.ToLower())&quot; -ContentType &quot;application/octet-stream&quot; -Method Put -Headers @{&quot;Authorization&quot;=$auth;&quot;Content-Length&quot;=$assembly.Length;&quot;Expect&quot;=&quot;&quot;} -InFile $assembly.FullName.ToLower()<br />

We will repeat this operation for all the children as well. Once the upload is completed, we will set the references between the parts. The according json string has been created while uploading the files.

<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/references/v1/setreference&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}<br />
1&lt;/pre&gt;<br />
So, now that all the data is in the cloud and the references are set, we can start the transformation of our files into to a web suitable format. We do this with the registration service, for each file, like this:<br />
&lt;pre&gt;1<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/viewingservice/v1/register&quot; -ContentType &quot;application/json&quot; -Method Post -Body $json -Headers @{&quot;Authorization&quot;=$auth}<br />

At the end, we will check the status of the transformation. This is not mandatory, but as some file may take a bit longer, this way, we can wait until everything is done.

<br />
Invoke-RestMethod -Uri &quot;https://developer.api.autodesk.com/viewingservice/v1/$base64/status&quot; -ContentType &quot;application/json&quot; -Method Get -Headers @{&quot;Authorization&quot;=$auth}<br />

The last two lines of the script just prints out the token and the assembly URN that might be helpful in case you like to use them in your HTML code for showing the assembly.

There are more API calls, but i hope that with this simple sample, you have something that works and might be useful, and you have a feel of how to use PowerShell for consuming the View And Data API.

Posted in PowerShell, ViewAndDataAPI | Leave a comment

Vault, View And Data API via powerJobs

ViewAndData

Wouldn’t it be cool to access your 3D models from the web? Autodesk came out with a new technology called View And Data API, which allows displaying several file formats directly in your web-browser without the need of any plugin. Here’s a sample.

I know, as soon the word “cloud” is spoken, some of you are getting skin irritations, but sooner or later, accessing data in a meaningful way will be mandatory.

With this post, I’d like to show you how the View And Data API can be consumed via PowerShell, and to be more precise, via powerJobs. The following job will take an assembly from Vault and upload the files to the cloud services so that it can be viewed. The steps are more or less simple: login, create a folder (bucket), upload the files, create the references between the files, and start the translation.

Now, the entire world is moving toward REST API, which is the de-facto API language used in the web/cloud. Microsoft added a handy command-let with PowerShell 3.0. The command-let is called Invoke-RestMethod.

And here is the job

Add-Log -Text ">> starting cloud upload job"
$ConsumerKey="your consumer key"
$ConsumerSecret="your consumer secret"
$bucketName = "coolorange" #must be lower case and/or numbers
$policy = "transient" #transient

$file = PrepareEnvironmentForFile "Suspension.iam"

Add-Log -Text "Log in to ViewAndData..."
$login = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/authentication/v1/authenticate" -ContentType "application/x-www-form-urlencoded" -Method Post -Body "client_id=$ConsumerKey&client_secret=$ConsumerSecret&grant_type=client_credentials"
$auth = $login.token_type + " " + $login.access_token
$json = '{"bucketKey":"'+$bucketName+'","policy":"'+$policy+'"}'
Add-Log -Text "Creating bucket $bucketName ..."
try{
$bucket = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/oss/v1/buckets" -ContentType "application/json" -Method Post -Body $json -Headers @{"Authorization"=$auth}
}
catch{}

Add-Log -Text "Downloading file $($file.Name)"
$localTempPath = "c:\Temp\webview"
$assembly = Get-VaultFile -File $file.'Full Path' -DownloadPath $localTempPath
Get-ChildItem $localTempPath -Recurse -File | ForEach-Object { $_.IsReadOnly = $false }

$json = '{ "master" : "urn:adsk.objects:os.object:coolorange/'+$assembly.Name+'", "dependencies" : ['

$localFullPath = $assembly.'Full Path'
Add-Log -Text "-$localFullPath"
$localFullPath = $localFullPath.Replace("$",$localTempPath).Replace("/","\")
Add-Log -Text "uploading $localFullPath ..."
$filesInWeb =  @()
$fileInWeb = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/oss/v1/buckets/$bucketName/objects/$($assembly.Name)" -ContentType "application/octet-stream" -Method Put -Headers @{"Authorization"=$auth;"Content-Length"=$assembly.'File Size';"Expect"=""} -InFile $localFullPath
$filesInWeb += $fileInWeb

$children = Get-VaultFileAssociations -File "$/Designs/Suspension/Suspension.iam" -Dependencies
foreach ($child in $children) {
  $json += '{ "file" : "urn:adsk.objects:os.object:coolorange/'+$child.Name+'", "metadata" : { "childPath" : "'+$child.Name+'", "parentPath" : "'+$assembly.Name+'" } },'
  $localFullPath = $child.'Full Path'
  $localFullPath = $localFullPath.Replace("$",$localTempPath).Replace("/","\")
  Add-Log -Text "uploading $localFullPath ..."
  $fileInWeb = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/oss/v1/buckets/$bucketName/objects/$($child.Name)" -ContentType "application/octet-stream" -Method Put -Headers @{"Authorization"=$auth;"Content-Length"=$child.'File Size';"Expect"=""} -InFile $localFullPath
  $filesInWeb += $fileInWeb
}
$json = $json.Substring(0,$json.Length-1)
$json += ']}'
Add-Log -Text "Set the references"
$references = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/references/v1/setreference" -ContentType "application/json" -Method Post -Body $json -Headers @{"Authorization"=$auth}

$services = @()
foreach($fileInWeb in $filesInWeb)
{
  $fileName = $fileInWeb.objects[0].key
  Add-Log -Text "Start conversion for $fileName"
  $fileName = [System.Web.HttpUtility]::UrlEncode($fileName)
  $urn = "urn:adsk.objects:os.object:$bucketName/$fileName"
  $base64 = [convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($urn))
  $json = '{"urn":"'+$base64+'"}'
  $service = Invoke-RestMethod -Uri "https://developer.api.autodesk.com/viewingservice/v1/register" -ContentType "application/json" -Method Post -Body $json -Headers @{"Authorization"=$auth}
  $services += $service
}

Add-Log -Text "<< Job completed!"

You can just copy paste it into a PS1 file within the C:\ProgramData\coolOrange\powerJobs\Jobs folder. Then go to an assembly in Vault and queue this job via powerJobs.

For the sake of simplicity, this job just takes care about one level of children. So, for multilevel assemblies, the job would have to be executed recursively for each level.

The key thing here is that you can see how to consume the View And Data API within PowerShell, and so create your own scripts, and how to combine this with Vault.

In order to see the uploaded files you need your own little web page, which is not part of this post. But if you like to know more about this technique, you can follow the quick start guide. If you like to get this job running on your side, you need to create your own account and place the consumer key and secret at the top of this script.

We love this stuff and will play more and talk more about this in the next months, so, in case you like this too, share your ideas with us!

 

Posted in powerJobs, PowerShell, ViewAndDataAPI | 1 Comment

New folder with subfolders

2015-02-20_00-16-46Some months ago, we made this post regarding creating a folder and automatically copying the subfolder tree from a template. In that post, the folder tree was inside Vault. We received several comments asking whether it would be possible to hold the folder tree outside Vault, for instance in an XML file. This post will show you how to do it.

In an XML file we define which templates are available and how the folder structure looks like. In this case the XML file is called Templates.xml and looks like this

<?xml version="1.0" encoding="utf-8"?>
<Templates>
<Template Name="Production">
   <Folder Name="Folder1" Category="Project">
     <Folder Name="SubFolder1" Category="Folder">
       <Folder Name="SubSubFolder1" Category="Base" />
     </Folder>
     <Folder Name="SubFolder2" Category="Project" />
   </Folder>
   <Folder Name="Folder2" Category="Project"></Folder>
   <Folder Name="Folder3" Category="Project"></Folder>
</Template>
<Template Name="Design">
   <Folder Name="Folder1" Category="Base">
     <Folder Name="SubFolder1" Category="Base">
       <Folder Name="SubSubFolder1" Category="Base" />
     </Folder>
     <Folder Name="SubFolder2" Category="Base" />
   </Folder>
   <Folder Name="Folder2" Category="Base"></Folder>
</Template>
<Template Name="Marketing">
   <Folder Name="Folder1">
     <Folder Name="SubFolder1" Category="Base">
       <Folder Name="SubSubFolder1" Category="Base" />
     </Folder>
     <Folder Name="SubFolder2" Category="Base" />
   </Folder>
   <Folder Name="Folder2" Category="Base"></Folder>
</Template>
</Templates>

As you can see, the first level of the XML contains the templates with an attribute name, while each template contains a nested folder tree structure with folder Name and folder Category.

The XAML file for the folder creation (Folder.xaml), will be extended by a combobox which reads the template names from the XML file. In order to do this, we add a resource in the MainWindow.Resources section of the XAML, like this

<XmlDataProvider x:Key="templates" Source="C:\ProgramData\Autodesk\Vault 2015 R2\Extensions\DataStandard\Vault\Configuration\FolderTemplates.xml" XPath="//Template"/>

later in the combobox, we can point to this resource and display the attribute name in the list, like this:

<ComboBox ItemsSource="{Binding Source={StaticResource templates}}" DisplayMemberPath="@Name" SelectedValuePath="@Name" SelectedValue="{Binding Prop[Template].Value}" IsEnabled="{Binding CreateMode}" Grid.Row="4" Grid.Column="1"/>

As you can see, we store the selected template in a custom property called Template, so that after the folder creation, we can identify the selected template and create the subfolders.

The PowerShell code is also fairly short. In the Menu/CreateFolder.ps1 we have a recursive function that goes into each level of the XML file and creates the according subfolders.

function recursivelyCreateFolders($childFolders, $newFolder,$folderCategories)
{
      if($childFolders -eq $null) { return }
      foreach ($folder in $childFolders) {
            $newSubFolder = $vault.DocumentServiceExtensions.AddFolderWithCategory($folder.Name, $newFolder.Id, $false, $folderCategories[$folder.Category])
            recursivelyCreateFolders -childFolders $folder.ChildNodes -newFolder $newSubFolder -folderCategories $folderCategories
      }
}

Later in the same file, we will retrieve the selected template, load the XML file with the template definitions, also load the category definitions as we will need them later and then start the recursion of above.

$template = $dialog.ViewModel.Prop["Template"].Value
if($template -ne "")
{
   [xml]$xml = Get-Content "C:\ProgramData\Autodesk\Vault 2015 R2\Extensions\DataStandard\Vault\Configuration\FolderTemplates.xml"
   $folderTree = Select-Xml -Xml $xml -XPath "//Template[@Name='$template']"
   $folderCats = $vault.CategoryService.GetCategoriesByEntityClassId("FLDR",$true)
   $fc = @{}
   $folderCats | ForEach-Object { $fc[$_.Name]=$_.Id}
   recursivelyCreateFolders -childFolders $folderTree.Node.ChildNodes -newFolder $newFolder -folderCategories $fc
}

That’s it. In summary, we have an XML that contains the folder template definitions, a combobox for selecting the template and some lines of PowerShell code in order to create subfolders based on the XML definitions. Now the user can create a new folder and pick a template. The new created folder will have the subfolders as defined in the XML file.

Here you can download the ZIP file with the complete code.

Have fun!

Posted in Data Standard | 1 Comment

Numbering with Data Standard

numbering

When using Vault Numbering Schemes without Data Standard you will notice some shortcomings. Just to name some examples: The user always sees all active numbering schemes when creating new file in Inventor or AutoCAD. It cannot be enforced that a numbering scheme is used when creating new files (the user can skip the dialog). Numbering schemes don’t have fields that can be controlled by properties.

With Data Standard 2015 R2 support for Vault Numbering Schemes has been introduced. Together with the adaptability of Data Standard this can be used to solve the challenges mentioned above.

A numbering scheme requires actually a dynamic number of different controls such as labels, text- and combo boxes depending on the definition. The good news is that you don’t have to define each of these controls in your XAML file. Instead you just need to add one line to get a numbering scheme control:

...
<WPF:DSNumSchemeCtrl Grid.Column="1" Grid.Row="8" Scheme="{Binding ElementName=NumSchms, Path=SelectedItem}" GeneratedNumberProperty="_GeneratedNumber"/>
...

The control has built-in validation. For example when you’ve defined a free text field in your numbering scheme that requires exactly three characters, it would look like this:

NumberingValidation

Please note that for Inventor and AutoCAD the native Vault numbering scheme dialog doesn’t come up anymore as long as Data Standard is enabled.

Let’s do some modifications to the standard behavior!
The control has two mandatory attributes, so called dependency properties. It’s basically an input and an output “parameter” for the control.

“Scheme” (input)
Here the name of the numbering scheme definition has to be specified.
In the sample delivered with Data Standard this is bound to the selected element of a combo box, which gets its values from the PowerShell function “GetNumSchms”. With this the list of numbering schemes can be easily filtered by modifying the “GetNumSchms” function.

The following sample shows different numbering schemes based on the selected category when using the function “New Standard File …” in Vault.

NumberingFilter
First of all we need to change the function “GetNumSchms”:

function GetNumSchms
{
    $ret = @()
    $global:numSchems = $vault.DocumentService.GetNumberingSchemesByType([Autodesk.Connectivity.WebServices.NumSchmType]::Activated)
    foreach ($item in $numSchems)
    {
        switch ($Prop["_Category"].Value)
        {
            "Engineering"
            {
                if ($item.Name.StartsWith("ENG")) {$ret += $item.Name}
                break
            }
            "Office"
            {
                if ($item.Name.StartsWith("OFF")) {$ret += $item.Name}
                break
            }
            "Standard"
            {
                if ($item.Name.StartsWith("STD")) {$ret += $item.Name}
                break
            }
            default {$ret += $item.Name}
        }
    }
    return $ret
}

To run the sample you need to define numbering schemes starting with “ENG”, “OFF” and “STD”. When the dialog is shown the function “GetNumSchms” gets called. Based on the selected category (bound to property “_Category”) it adds only specific numbering schemes to the list that gets returned by this function.

The sample is not quite working yet. The combo box that shows the available numbering schemes has to be updated when another category is selected. To do this we need an event handler that gets called when the property “_Category” changes.

...
function InitializeWindow
{
    ...
    $Prop["_Category"].add_PropertyChanged({
                    param($object, $arg)
                    Category_OnPropertyChanged $arg
    })
}
...

function Category_OnPropertyChanged
{
    param($arg)
    $argname = $arg.PropertyName
    if($arg.PropertyName -eq "Value")
    {
        $numSchmCtrl = $dsWindow.FindName("NumSchms")
        $numSchmCtrl.GetBindingExpression([System.Windows.Controls.ComboBox]::ItemsSourceProperty).UpdateTarget()
        $numSchmCtrl.SelectedIndex = 0
    }
}

“GeneratedNumberProperty” (output)
Here you have to tell the control to which property the generated number has to be saved, once the user clicks the OK button.
You still have the chance to modify the generated number before the file gets created.

For Inventor or AutoCAD the “GeneratedNumberProperty” is set to “DocNumber”. The following sample Inventor.cfg shows how to add the value of the title property to the generated number (don’t know if that makes sense but you get the point):

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
    <PathDefinition>{Workspace}\{Prop[Folder].Value}</PathDefinition>
    <FileNameDefinition>{Prop[DocNumber].Value}-{Prop[Title].Value}</FileNameDefinition>
    <PropertyDefinitions>
        <PropertyDefinition PropertyName="DocNumber" DataType="Text" InitialValue="{PathAndFileNameHandler.FileName}" />
...

To do more sophisticated things you can override the PowerShell function “OnPostCloseDialog” for Inventor and AutoCAD or the “GetNewFileName” function for Vault. These functions can be found in the “default.ps1″ file.

I hope this information was helpful to get started with numbering schemes in Data Standard.

 

 

Posted in Data Standard | Leave a comment