Azure Uploader for Power Platform is here

Azure Storage with its high availability and numerous features has always been a storage of choice for many requirements. When it comes to Power Platform we should carefully weigh the options that we have for storing files. Besides the native Dataverse that is fully integrated with the platform, but comes with the highest costs, there are other alternatives like SharePoint and Blob storage in Azure Storage.

Diagram displays a horizontal line with three logos displayed on top. On the left, the line points to Best integration,
Power Platform storage options comparison

Azure Uploader is a PCF control that is built to reduce the effort of integrating with Azure’s Blob storage. It makes it easy to upload files with virtually no limit in size, directly to blob storage in a secure way.

You might be wondering why directly from the browser and what about the security. To store files in blob container you have two major options.

  • The first option is to upload the file to a proxy service and the proxy service would then upload the file to the storage behind the scene. This gives you the advantage of being able to do some processing (e.g. validate the content) before storing the file. But it comes with the extra effort required to build the service and maintain it over time. In addition to that you need to make sure this new service has the required availability (or SLA) required for your application. As the old saying goes, you will have one more layer that things can go wrong.
  • The second option is to have a very simple proxy service (that you might already have) generate a short-lived SAS token with limited access, share it with the client and the client would take care of uploading the file. This way you can rely on the availability and capacity of Azure Storage service.

You should consider that files coming from the client cannot be trusted, so you might need to put in place a process to validate them before use. In the first approach the proxy service is able to validate files during the transfer, but in the second approach you should do this validation later. For example a Power Automate flow, a Logic App, or an Azure Function App can be triggered and do this asynchronously.

Azure Uploader relies on the second approach. This means that you just need to generate a SAS token in your app and share it with the uploader. Generating a SAS token is very easy thanks to the out-of-the-box Azure Storage Blob connector.

The following Power FX code, generates a new SAS token using Azure Storage Blob connector (called AzureBlobStorage) and stores it in a variable called SasToken.

Set(
    SasResult,
    AzureBlobStorage.CreateShareLinkByPathV2(
        "mystorageaccount",
        "/mycontainer",
        {
            AccessProtocol: "HttpsOnly",
            ExpiryTime: DateAdd(Now(), Hours, 1),
            Permissions:"Write,Add,Create"
        }
    )
);
Set(
    SasToken,
    Mid(SasResult.WebUrl, Find("?", SasResult.WebUrl) + 1)
);
  • The above code, generates a code that is valid only for one hour DateAdd(Now(), Hours, 1) and only has Write,Add,Create permissions.
  • The SasResult variable that is set in the first step contains a property WebUrl which holds a URL that has the SAS token in its query string. That is why the line Mid(SasResult.WebUrl, Find("?", SasResult.WebUrl) +1) is used to extract only the part after “?” store it in SasToken variable.

NovoDocx – An open source service that generates documents using standard Word templates

Lately, as part of solution I have been building for a client, we had to deal with several document generation flows. I’m talking about really complicated document templates. For example there is one that is composed of several tables and sections that compare different credits / mortgages that a client can take and their financial impact, benefits and downsides and more. This is nothing like the simple mortgage documents that your local bank shares with you. In fact, I am happy for their clients that will receive such well-thought documents, but as for the poor developers, this was not an easy task.

There are two out-of-the-box options when you need to populate Word documents.

  • Word templates feature that existed for ages in Dynamics CRM and now inherited by Power Platform. This is normally the easiest option, but it has many limitation and only works in harmony with rows in a table and its direct related tables.
  • Word Online Connector which is a premium connectors and more capable without dependency on any table. The main issue with this one is when you have flows in your solution that will be deployed in different environments or any time the location of your document needs to change. In this case instead of the name of these placeholders you would need to use random numbers because this connector cannot figure out the schema of the document or lock it after your build one.

For the second options there are some workaround, that I will be writing about soon in future, but what if the logic is so complex that using Flows or a Power App does not make sense or not enough.

In search for an open source library

At this point I started looking for an open source .NET library (preferably) to let us get the job done. It should exist in this day and age, right? The answer is no, it doesn’t. There are only some paid libraries and services that are quite expensive for what they do and working with them is not as easy as one might think. After digging deeper into GitHub, I was able to find some libraries that either rely on Open Office or a headless browser or the brain of the library is in a closed-source black box. I have to say honestly the fact that there is no open-source library and not in .NET was annoying enough, so I decided to start one and I decided to start simple and make it as easy as possible for the developer to use with no knowledge of the underlying format (😎 ehem which I know pretty well, but not to Eric White level well ☺).

Novo Docx

NovoDocx’s first readme file push in Git

Today I share with you my first iteration of Novo Docx, my take on a simple-to-use and simple-to-host library and service that you can use in you own projects. The current features are enough to handle a complex documents and quite fast. I have tried to capture many edge cases too. I will be focusing on enriching the functionality and providing more hosting options. The source code is hosted in GitHub. It includes a simple library and an Azure Functions App that you can use however you like.

Build dialogs using model-driven pages to run flows all with low-code

Low-code is all the rush these days. It makes for more maintainable solutions that are built faster, specially when it comes to extending existing platforms like Power Platforms / Dynamics. It makes our work more decoupled from that of Microsoft’s and we can each focus on building better solutions without treading on each other’s toes. 😊

One of the main extension points in Power Platform is adding custom buttons to the Command bar. If you are not fully familiar with command bar, be sure to read the following article in the official docs to know what it is and where it is before reading further.

Command designer overview – Power Apps | Microsoft Docs

The previous experience was called Ribbon which is still around. If you would like to know more about this evolution, please read the following page from the official docs.

Command bar or ribbon presentation (model-driven apps) – Power Apps | Microsoft Docs

What are we building?

  • Reusable JavaScript library to open custom pages as dialog boxes
  • Custom button in command bar
  • Custom page in model-driven app to act as a dialog box
  • A cloud flow

In case you are wondering if this is a good practice to display custom pages in dialog boxes, this whole thing is recommended by Microsoft, but the only caveat right now is that Microsoft has not (yet?) provided a low-code solution to open custom pages. Instead, they have given us a list of JavaScript samples that can be used as starting points. I specially recommend you read the first article to have a good idea of where we are headed.

A cloud flow that can be triggered by apps

First things first, right? If we are building a dialog box to run cloud flows, we would first need a cloud flow. Anything would do, just remember it should be a flow that can be triggered by Power Apps and I strongly recommend to respond back with the result in your flow.

You’ll notice in the following screenshot that I have a flow that uses PowerApps (V2) as trigger and at the end it is returning two parameters as a result to any app that calls the flow.

Adding a Respond to a Power App or Flow at the end will let the caller (app or flow) to know if your flow has succeeded or not and the caller will have a chance to react accordingly.

A custom page in your model-driven app

Next step is to build a custom page that can be used as a dialog box. The goal of this custom page (which is a kind od Canvas app with some more power from model-driven apps by the way) is to help end-user run a flow (or several flows) and wait for them to complete while displaying a spinner and once the flow complete let the user know if something went wrong or simply disappear and refresh the data in the form.

To add a custom page to your model-driven app, you need to open the app in the new modern editor and click on the New Page button.

I suggest you build your custom page the responsive way, so you can display it in any kind of dialog no matter what size. To make a responsive page, I take the following approach.

As you can see on the image, I have one screen that contains a vertical container, an image called BusySpinner, a rectangle that fills the entire page to hide the content when the flow is ongoing.

In the VerticalContainer, I have two horizontal containers, the first one contains the only input parameter (language) I need to ask to user and the last one contains the OkButton. In your case you might need several horizontal containers. One per each parameter.

The visual layout of the page is similar to the following diagram. If it’s not clear enough, let me know in the comments and I will write all the properties you might need in a table here.

You will two variables to store the state of the page, IsBusy and IsValid.

  • IsBusy – this variable will have the value true when the cloud flow is executing and false when not.
  • IsValid – this variable will be false by default and will be true when all the required parameters are given by the end user and they are valid.

In your app’s OnStart you need the following commands to initialize the variables. I use this event to also initialize a simple collection I will use to fill a combo-box for my language parameter.

Set(IsValid, Not(IsBlank(Param("recordId"))));
Set(IsBusy, false);
If(Not(IsValid), Notify("Opportunity is not selected!"));
ClearCollect(LanguageCodes, "EN", "FR", "DE", "PT");

Next, you’ll need to add the cloud flow to your page, once you do that, you can trigger it in the OnSelect event of the OkButton.

Set(IsBusy, true);
Set(FlowResult,'Generatemandatedocument'.Run(Param("recordId"), LanguageOptions.Selected.Value));
If(FlowResult.succeeded, 
    Notify(FlowResult.message, NotificationType.Success);Back(), 
    Notify(FlowResult.message, NotificationType.Error));
Set(IsBusy, false);

As you have noticed in the above code, I’m setting the IsBusy variable to true then I start the flow, while putting its result in a variable called FlowResult, this allows me to check the result of the flow in the line that follows. If the result is successful, I simply close the page (dialog), otherwise I’ll inform the user. At the end I set IsBusy to false.

To display the spinner and fade the content behind, when the flow is ongoing, you simply need to put the IsBusy in the Visible property of both BusySpinner and BusyOverlay.

You can also use the following expression in the DisplayMode property of the OkButton. This will disable the button when the flow is ongoing or when the input parameters are not valid.

If(IsValid And Not(IsBusy), DisplayMode.Edit, DisplayMode.Disabled)

Reusable JavaScript library to show custom pages in dialogs

Save the following script in a .js file (e.g. dialogs.js) and then open your solution in https://make.powerapps.com, and click on New, from the drop-down menu, open More and click on Web resource.

/* This function uses Navigation API to display a centered dialog. */
/* Simple parameters are used instead of object to make the function compatible with command bar parameters. */
function displayDialog(rowId, tableName, pageName, title, contentWidth, contentHeight, primaryControl) {
  const parseSize = (str, defaultStr) => (str || defaultStr || "500px").match(/([\d\.]*)(.*)/).splice(1,2);
  let [w, wUnit] = parseSize(contentWidth);
  let [h, hUnit] = parseSize(contentHeight);
    var pageInput = {
      pageType: "custom",
      name: pageName,
      entityName: tableName,
      recordId: rowId.replace(/[{}]/g, "")
    };
    var navigationOptions = {
      target: 2, 
      position: 1,
      width: {value: parseFloat(w), unit: wUnit},
      height: {value: parseFloat(h), unit: hUnit},
      title: title
    };
    Xrm.Navigation.navigateTo(pageInput, navigationOptions)
      .then(
        function () {
            if (primaryControl.getFormContext) { primaryControl.getFormContext().data.refresh(); }
            else {primaryControl.data.refresh();}
        }
      ).catch(
        function (error) {
          /* TODO: Add exception handling here. */
        }
      );
}
https://gist.github.com/rezanid/33d22ae5381d1d159737a9645d7be098.js

The above code is based on the Centered Dialog sample of Microsoft. I have only made a bit more production ready.

  • The whole script is a function that can be easily called from the new Command bar designer (still in preview).
  • Width and height have default values to be used in case no value is given by the custom button.
  • Curly braces are removed from rowId to make it Power App friendly.
  • When the dialog box closes, the content is refreshed.

Pay attention to lines 23 and 24 and see how I’m getting to the formContext object. It is done that way so you can use the same script from any flavor of command bar.

I recommend you improve the script to cover for some edge cases and more importantly add exception handling to notify the end-user properly if “something went wrong“. ☠

Adding a custom button to command bar

The new command bar editor can be found in the modern app editor (which is in preview at the time of this writing). You need to select an app in your solution and click on the three dots to open it context menu and select Edit in preview item. Just like the following screenshot.

Once in the new App editor, scroll down to the table where you its command bar to receive this fancy new button. In the following screenshot, you’ll notice I’m editing the command bar of Opportunity table.

Power Platform will ask you which command bar you are interested in. I am going to choose Main form, but in case you are wondering what are these options, you need to read the first link I have shared in the beginning of my post 😉.

Once in the command bar editor, add a new button with the following properties.

PropertyValue
LabelRun my flow (can be anything you want).
ActionRun JavaScript (in future I hope we will be able to use PowerFx instead).
LibraryThe name you gave to your web resource. I used fancy_dialogs for example.
NOTE!
If you can’t find your library in the list you will need to click on the pen icon just besides the drop-down menu.
Function namedisplayDialog
Parameter 1FirstPrimaryItemId
Parameter 2String | opporunity (this should be the logical name of your table)
Parameter 3String | fancy_flowlauncher (this should be the name of your custom page)
Parameter 4String | Run flow (this will be the title of your dialog)
Parameter 5String | 500px
Parameter 6String | 500px
Parameter 7PrimaryControl

If you have followed all the steps correctly, the end result should be similar to the following.

Power Platform Tidbits #2 – What is possible with business rules in Model-Driven Apps vs Canvas Apps

Business rules can be used by Canvas Apps or Model-driven Apps to do the following:

  • Set column values.
  • Clear column values.
  • Validate data and show error messages.

But keep in mind that they can do the following only in model-driven apps:

  • Show or hide columns.
  • Enable or disable columns.
  • Create business recommendations based on business intelligence

Reference: Define and create business rules in Dataverse

Power Platform Tidbits #1 – Business Rules vs Client-side scripts in model-driven apps

Model-driven App Designer image

When deciding between business rules and client-side scripts in Model-driven Power Apps, the best practice is to use business rules are much as possible and only resort to writing code when necessary. The following table lists some of the most important reasons you might choose one over the other.

Business RulesClient-side scripts
Run business logic on loadRun business logic on save
Set field values in formInstantly update fields in form
Show / hide fields in formFetch related data