Folder Management – Visual Studio Online Extension, by Wouter De Kort


As one of the ALM Rangers, Wouter had the opportunity to join the early access program for the new extension model for Visual Studio Online.

After the storyboard and with his extension idea approved by the product group, he started building the new extension – Folder Management.

The idea is simple: create new folders directly from the code part of web access. Previously you could only create new files from web access. To add a folder, you would need to use your local Git or TFVC repository. This meant that people who aren’t comfortable using Git or Visual Studio, couldn’t add a folder.

The Folder Management Extension helps with this. By right clicking on your project root or a subfolder, you can easily create a new folder.

clip_image002clip_image004

The dialog allows you to enter the folder name and a comment.

clip_image006

How does it work?

A Visual Studio Online extension has a manifest file and HTML, CSS and JavaScript files. For this extension, Wouter chose to use TypeScript to write the JavaScript.

The manifest file describes your extension like its name, description and icon. The manifest also configures the extension points that your extension uses.

{

  "namespace": "almrangers.vsoextensions.FolderManagement",

  "version": "1.0.0",

  "name": "Folder Management", 

  "description": "An extension for Visual Studio Online that adds the ability to

                  create new folders directly from web access in the code view",

  "provider": {

    "name": "ALM Rangers - Wouter de Kort"

  },

  "baseUri": "https://foldermanagement-vsoextensions-almrangers-azurewebsites.net/",

  "icon": "https://foldermanagement-vsoextensions-almrangers.azurewebsites.net/images/
           VSO_Folder_196x.png"
,

  "contributions": {

    "vss.code.web#sourceTreeItemActions": [

      {

        "id": "addFolder",

        "text": "Create a new folder",

        "title": "New Folder",

        "icon": "images/VSO_Folder_16x.png",

        "group": "actions",

        "uri": "main.html"

      }

    ],

    "#dialog": [

      {

        "id": "createNewFolderDialog",

        "uri": "dialog.html"

      }

    ]

  },

  "contributionPoints": {

    "dialog": {

      "type": "vss.web#control",

      "description": "Provider of contribution controls."

    }

  }

}

As you can see, this extension extends the sourceTreeItemActions with a new menu option. It also declares a dialog and points to the HTML page with the markup for the dialog.

When a user right clicks on in the code web access, VSO loads your extension. When you keep your Developer Tools in your browser open you’ll see that the scripts for your extension are loaded on demand. The first picture shows the scripts loaded after VSO finishes loading your code page. The second one shows the scripts after you right clicked on a folder and VSO loaded the extension code.

clip_image008

clip_image010

As you see, the localhost node is new. This is the location where the scripts are located on the machine. You can now add breakpoints and inspect the code that forms your extension. What’s cool about this is that you can run your extension locally in Visual Studio. If you refresh your browser (which points to your VSO account), the extension gets loaded from your local pc.

Your extension is loaded by triggering the main.html (you can name it whatever you want, as long as it matches your manifest) file:

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Folder Management</title>

</head>

<body>

    <div>Users will never see this page.</div>

 

    <script src="sdk/scripts/VSS.SDK.js"></script>

    <script src="scripts/FolderManager.js"></script>

    <script src="scripts/GitFolderManager.js"></script>

    <script src="scripts/TFVCFolderManager.js"></script>

    <script src="scripts/main.js"></script>

    <script>

        VSS.init({ setupModuleLoader: true });

    </script>

</body>

</html>

The first JavaScript file is the VSS SDK that you need to build your extensions. The lines after that load the JavaScript code in the extension. The only other thing that’s required is a call to VS.init to start your extension.

This is enough to add the menu option to VSO. But of course you want something to happen once you click on it. If you look in main.ts you see the following code at the end of the file:

VSS.register("addFolder", function (context) {

    return new AddFolderMenu();

});

This attaches the AddFolderMenu class to the addFolder action. Now whenever a user clicks on the new menu option, the execute method on the AddFolderMenu class runs:

public execute(actionContext) {
    this.actionContext = actionContext;
   
this
.showDialog();
}

From this point on your extension is in control. You can now start running code and doing all kinds of things. In this case, the actionContext parameter is saved and a dialog is shown.

ActionContext is an important parameter. This gets passed from VSO to your extension. In this case, actionContext tells you if the user is running a TFVC or Git project and where in the folder structure the user started your extension from.

The showDialog method uses the VSS SDK to configure a dialog and show it. The dialog configuration is in the JSON manifest file and points to dialog.html. The following code loads the dialog service:

VSS.getService("vss.dialogs").then((dialogSvc: IHostDialogService) => { … }

Inside this method, you configure the dialog and show it to the user. VSO automatically creates a dialog for you with a title, ok and cancel buttons and a resizable form.

One important method on the dialog is updateOkButton. This method allows you to enable and disable the Create button of the dialog. I’ve bound an event to the dialog that’s updated whenever a user changes the folder name. As soon as the folder name is empty, the Create button is disabled.

A second important property is the callback. This is the method that gets executed when the user clicks on Create. With a simple if statement, the callback points to the TFVCFolderManager or the GitFolderManager. Both have a callback method with the same signature. The internals of the methods however differ because of the way a folder is created in both Git and TFVC.

Creating the folder

When the callback fires, the user has selected a location, entered a name and clicked on Create.

Since the APIs are constantly evolving the team hasn’t finished a VSS Client yet for adding commits to Git or Changesets to TFVC. But you can always use the regular REST APIs that are available in VSO. To make this work, you need to add an authentication token to your REST requests just as you would do if you use the REST APIs from outside an extension. The following code sets op the authentication token:

VSS.require(["VSS/Authentication/Services"],(service) => {

    var authTokenManager = service.authTokenManager;

    authTokenManager.getToken().then((token) => {

        var header = authTokenManager.getAuthorizationHeader(token);

        $.ajaxSetup({

            headers: { 'Authorization': header }

        });

The ajaxSetup method makes sure that all AJAX requests from this point on have the correct header. Now you can use the following code to get a list of Items from TFVC:

$.ajax(tfvcItemsApiUri).then((changesets) => { … })

And that’s all there is to it. A POST call to the same URI with a JSON object as content is all there is to add new items to your repository.

In this case, it was a little more difficult because the documentation for the APIs is not finished yet. By using Fiddler, Wouter could find the API details for creating a new file. With some slight changes and help from the team this was easily changed to add a new folder.

Lessons learned

There are a couple of key takeaways that were learned from creating this extension:

  • Use TypeScript. This is not a requirement but it makes it much easier to discover the APIs and prevent mistakes
  • Study the samples. There are a couple of samples online. These samples aren’t documented yet so you have to do some manual exploration to find everything. However, the samples are very complete and you can find a lot of detail in them.
  • Explore the REST APIs. When you use the manual authorization token, you can call all REST APIs that are available on VSO. This opens up a range of possibilities.
  • Start building now!

The extension model is very well thought out. Creating an extension is not very hard. It requires some research but as the documentation gets more complete and more sample code is added this will become easier.

So what did you always miss in VSO? Why wait for the product team to create it?


Authored by Wouter De Kort
I'm a lead architect and consultant working at Ordina in the Netherlands. I focus on helping customers adopt ALM practices. In addition to working at Ordina I have my own company, Seize IT. Through Seize IT I wrote a couple of books for Microsoft Press on C#, Azure and Windows Apps. I'm also a Microsoft Certified Trainer for the whole Microsoft Web Stack. You can reach me at twitter (@wouterdekort) or on my blog: http://wouterdekort.blogspot.com.


Comments (4)

  1. Anonymous says:

    Hi Wouter,

    Nice article and extension! I hope other extensions will be added soon…

  2. Kumar says:

    Hi Wouter,

    Is there a way to create folder using rest api in vso using git.

    appreciate your help

    1. Hi Kumar,

      yes you can absolutely create a folder through REST in a Git repository. The REST API is used by Folder Management do this both for Git and TFVC. If you look at the code on GitHub (https://github.com/ALM-Rangers/Folder-Management-Extension/blob/master/src/FolderManagement/scripts/GitFolderManager.ts) you can see how the REST API gets called.

      Does that help?

      Wouter

  3. Thank you for this much needed extension! This does exactly what I need, simply and cleanly.
    It has been inefficient to configure a new project with some default folders until now.
    I know there are other ways to do this using a local project…but that is not the most efficient way.
    Well done, Wouter, and thank you!

Skip to main content