Power Pages: Tracking clicks and downloads

Knowing how your external users are using you Power Pages is very important. Especially if you are sharing import documents through the Power Pages. In this blog post, we will show you how to set up a Power Page with a script that monitors the downloads and stores this data in Dataverse. By leveraging this functionality, you can gain valuable insights into user behaviour, track file downloads, and make data-driven decisions.

This solution was created together with my colleague Rik de Koning who speaks regularly at Power Platform events.

Power Pages configuration

Before we can save data to the Dataverse with a JavaScript we need to configure the Power Pages to allow the web api access. You need to configure the first 2 settings for each Dataverse table where data needs to be stored.

  • Open the Portal Management of your Power Pages.
  • Go to Site Settings
  • Here you need to add three settings.
  • The first setting is to enable the web api on your Dataverse table.
    • Name: Webapi/[table logical name]/enabled
    • Website: Select the website
    • Value: true
  • The second setting is to allow the web api to access the fields
    • Name: Webapi/[table logical name]/fields
    • Website: Select the website
    • Value: cre9b_userid,cre9b_document,cre9b_username,cre9b_dateandtime
  • The third setting is to allow for errors to be displayed.
    • Name: Webapi/error/innererror
    • Website: Select the website
    • Value: true

The JavaScript

In our example we created a button that both downloads a document (note with an attached file) and creates a new record in a Dataverse table. The api call to create the record is added as a onclick event on the button, while the href is linked to the actual document. The JavaScript itself needs to be added on the content page linked to the Information page. If you have another use case please look at the details of the Portal Web API on Learn Microsoft.

  • Open the Portal Management and open the Web Pages
  • Open the Information page where the JavaScript needs to be added.
  • Open the related Content page.
  • First you will need to add the Wrapper AJAX function, this will give you the function safeAjax to use the web api.
(function(webapi, $){
    function safeAjax(ajaxOptions) {
        var deferredAjax = $.Deferred();

        shell.getTokenDeferred().done(function (token) {
            // add headers for AJAX
            if (!ajaxOptions.headers) {
                $.extend(ajaxOptions, {
                    headers: {
                        "__RequestVerificationToken": token
                    }
                }); 
            } else {
                ajaxOptions.headers["__RequestVerificationToken"] = token;
            }
            $.ajax(ajaxOptions)
                .done(function(data, textStatus, jqXHR) {
                    validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
                }).fail(deferredAjax.reject); //AJAX
        }).fail(function () {
            deferredAjax.rejectWith(this, arguments); // on token failure pass the token AJAX and args
        });

        return deferredAjax.promise();	
    }
    webapi.safeAjax = safeAjax;
})(window.webapi = window.webapi || {}, jQuery);
  • You can now add a function to the script that gathers the required information and send that to the safeAjax funtion. The safeAjax function then executes the api call.
function openfile(url) {
	var nowDateTime = new Date();
	var username = "{{ user.fullname }}";
	var contactid = "{{user.contactid}}";

    webapi.safeAjax({
		type: "POST",
		url: "/_api/[tablename]",
		contentType: "application/json",
		data: JSON.stringify({
			"cre9b_userid": contactid,
			"cre9b_document": url,
			"cre9b_username": username,
			"cre9b_dateandtime": nowDateTime
			
		}),
		success: function (res, status, xhr) {
      //print id of newly created table record
			console.log("entityID: "+ xhr.getResponseHeader("entityid"))
		}
	});
}
  • You can use the {{ user.[column] }} to get data out of the contacts table.
  • url is not a default options, I created a variable in a different part of the JavaScript.

For everybody that is interested in how we added the buttons I have added that code as well. With a JavaScript we created a button for each file (note) by changing the URL column into a button.

$('.entitylist').on("loaded", function () {
    // this will get the index of the corresponding table header
    var columnindex = $('th:contains("URL")').index();
    // this will loop through each table row below the corresponding table header
    $('tr').each(function () {
        var column = $('td', this).eq(columnindex);
        if (column.text().includes("https://")) {
            // this will find all a attributes based on the document links link
            const documentLinks = document.querySelectorAll(`[href="${column.text()}"]`)
            documentLinks.forEach(documentLink => {
                // this will set the className and the text of the link accordingly
                documentLink.className = "btnViewDocument"
                documentLink.target = "_blank"
                documentLink.text = "View"
                documentLink.onclick = function() { openfile(documentLink.href);};
            });
        }
    });
    // this will clear the name of the corresponding table header
    document.querySelector(`[aria-label*="URL"]`).innerHTML = ""
    document.querySelector(`[aria-label*="Created On"]`).innerHTML = "Shared On"
});

Power Platform: Custom administrator and developer role

Custom security roles on Power Platform are mostly used for Dynamics and model-driven apps, but they also work for canvas apps. By default an environment (without a Dataverse database) has two default roles (environment maker and environment admin). However if you create and environment with a Dataverse database, you get the ability to create custom security roles and 10 default roles. I strongly advise not to change the default roles.

Custom administrator role

At the moment of writing this blog it is possible to alter the default environment maker role (not system administrator), but I would not recommend it. Microsoft might push changes to the default roles and overwrite the customizations.

Creating a copy of the system administrator role or the environment maker role and making changes to the copied role, is also not an option. In the background Microsoft sets the CanEdit privilege to the administrator/environment maker role, and if you copy the role the CanEdit privilege is lost. The CanEdit privilege can only be set by Microsoft.

This practically means that creating a custom administrator role is not possible.

Note: Granting a user a role that effects the CanEdit privilege will take a non-specified amount of time to take effect. For example, if you switch from a copied role to a default role, it can take 30 minutes for the change to take effect.

Custom developer role

Creating a custom developer role is possible if you are willing to accept the following scenario. The developer gets an custom security role granting the required privileges, for example the ability to work with solutions and canvas apps but no export privileges. With only this security role the developer cannot access the environment and is missing the hidden CanEdit role.

The CanEdit role can also be granted by being an owner or a co-owner of a canvas app in the environment. If an administrator creates a canvas app and makes the developer co-owner of that app then the developer can access the environment and has the hidden CanEdit role.

Teams / Power Automate adaptive cards

When sharing information or sending out a notification on teams I like to use the adaptive card feature. Adaptive Cards are the Teams method of sharing and displaying blocks of information in an easy-to-read manor without the complexity of customizing CSS or HTML to render them. With adaptive cards you can even create polls, show weather information, and create hyperlinks.

Creating a feedback form

  • Create a Power Automate flow with the required trigger.
  • Add the Post adaptive card in a chat or channel Teams action.
  • Select the Group chat (Channel or Group Chat) or provide the chat ID.
  • Add the following JSON code for an example feedback form with a title, single line, and multi-line text input fields and two types of choice list.
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.0",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "weight": "Bolder",
      "id": "Title",
      "text": "EXAMPLE FEEDBACK FORM",
      "horizontalAlignment": "Left"
    },
    {
      "type": "Input.Text",
      "placeholder": "{acFullName}",
      "style": "text",
      "isMultiline": false,
      "maxLength": 75,
      "id": "acFullName"
    },
    {
      "type": "Input.Text",
      "placeholder": "{acComments}",
      "style": "text",
      "isMultiline": true,
      "maxLength": 200,
      "id": "acComments"
    },
    {
      "type": "TextBlock",
      "size": "Medium",
      "weight": "Bolder",
      "text": "Do you like Adaptive Cards?",
      "horizontalAlignment": "Left",
      "separator": true
    },
    {
      "type": "Input.ChoiceSet",
      "id": "acDecision",
      "value": "1",
      "choices": [
        {
          "title": "Yes!",
          "value": "Yes"
        },
        {
          "title": "Of course!",
          "value": "Of course"
        }
      ],
      "style": "expanded"
    },
    {
      "type": "TextBlock",
      "text": "Suggest follow-up discussion regarding:",
      "weight": "Bolder"
    },
    {
      "type": "Input.ChoiceSet",
      "id": "acFollowUp",
      "isMultiSelect": true,
      "value": "",
      "choices": [
        {
          "title": "Everything",
          "value": "Everything"
        },
        {
          "title": "Always",
          "value": "Always"
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.Submit",
      "title": "Submit"
    }
  ]
}

Creating a Poll

  • Create a Power Automate flow with the required trigger.
  • Add the Post adaptive card in a chat or channel Teams action.
  • Select the Group chat (Channel or Group Chat) or provide the chat ID.
  • Add the following JSON code for an example Poll with a title, header line, header, text, and a short poll.
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "TextBlock",
            "text": "Example Poll Request",
            "id": "Title",
            "spacing": "Medium",
            "horizontalAlignment": "Center",
            "size": "ExtraLarge",
            "weight": "Bolder",
            "color": "Accent"
        },
        {
            "type": "TextBlock",
            "text": "Example Header Tagline Text",
            "id": "acHeaderTagLine",
            "separator": true
        },
        {
            "type": "TextBlock",
            "text": "Example  Poll Header",
            "weight": "Bolder",
            "size": "ExtraLarge",
            "spacing": "None",
            "id": "acHeader"
        },
        {
            "type": "TextBlock",
            "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vestibulum lorem eget neque sollicitudin, quis malesuada felis ultrices. ",
            "id": "acInstructions",
            "wrap": true
        },
        {
            "type": "TextBlock",
            "text": "Example Poll Question",
            "id": "acPollQuestion"
        },
        {
            "type": "Input.ChoiceSet",
            "placeholder": "Select from these choices",
            "choices": [
                {
                    "title": "Choice 1",
                    "value": "Choice 1"
                },
                {
                    "title": "Choice 2",
                    "value": "Choice 2"
                }
            ],
            "id": "acPollChoices",
            "style": "expanded"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Submit",
            "id": "btnSubmit"
        }
    ]
}

Creating a hyperlink

  • Create a Power Automate flow with the required trigger.
  • Add the Post adaptive card in a chat or channel Teams action.
  • Select the Group chat (Channel or Group Chat) or provide the chat ID.
  • You can use dynamics content for the hyperlink text and the hyperlink itself.
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
       {
            "type": "TextBlock",
            "text": "This is example 1 [Hyperlink Texts](https://powerautomate.microsoft.com/)",
            "id": "acHeader",
            "wrap": true
        },
       {
            "type": "TextBlock",
            "text": "This is example 2 [@{triggerOutputs()['headers']['x-ms-user-name-encoded']}](https://powerautomate.microsoft.com/)",
            "id": "acHeader",
            "wrap": true
        }
    ]
}

Finding the chat ID

  • There are multiple ways to find the chat ID, I like to use the following way.
  • Open teams in the browser and open the chat
  • In the URL you can see the ID of the chat. You need to copy the code after conversations including the @ and that is after the @.
  • In my first example the ID is: 19:de13fe3a9cae407ba31abc84421e9ab4@thread.v2
  • In my second example the ID is: 19:95b0d2cb-aa0c-4e0c-8fcf-2f7b77c5afdb_d523c084-0c04-41d0-81d2-943ad42abe9a@unq.gbl.spaces
  • The action in Power Automate with find the chat and show the name of that chat.
https://teams.microsoft.com/_#/conversations/19:de13fe3a9cae407ba31abc84421e9ab4@thread.v2?ctx=chat
https://teams.microsoft.com/_#/conversations/19:95b0d2cb-aa0c-4e0c-8fcf-2f7b77c5afdb_d523c084-0c04-41d0-81d2-943ad42abe9a@unq.gbl.spaces?ctx=chat

Power Automate: Custom Flow loging

When managing multiple Power Automate Cloud Flows it can easily become a big task to figure out what went wrong and where. My client has more than 10 Cloud Flows that run multiple time per day or per hour. The solution is to create a log table (dataverse) or list (SharePoint) where all the runs are stored. The log table contains all the data an administrator needs to maintain the Cloud Flows. In my example I create a Dataverse table.

The Dataverse log table

In the log table we store the following data.

The Cloud Flow

In every Cloud flow a try and catch scope is added. All the main actions are in the try scope, if one of these actions fails than the catch scope will be used.

  • Add a Scope with the name Try.
  • Add the action the Dataverse Add a new row action.
  • Select the log table by Table name.
  • Select the Log status Processing, I use Processing, Failed and Successful.
  • Add the Cloud Flow name to Process name.
  • Add the following code to the Environment field.
workflow()?['tags']?['environmentName']
  • Add the following code to External URL, use the code snippets for the fx code.
    This creates a link to the actual flow run.
workflow()?['tags']?['environmentName']
workflow()?['name']
workflow()?['run']?['name']
  • Set the Status Reason to Active.
  • Add the end of the try scope add the Dataverse Update a row action.
  • Select the log table by Table name.
  • Add the ID of the create log in the Row ID field.
  • Select Successful in the Log status field.
  • Add a Scope with the name catch.
  • Add the Filter Array action.
  • Add the result of the try scope in the From field.
result('Try')
  • Add the following filter Status is equal to Failed
item()?['status']
  • Add the Dataverse Update a row action.
  • Select the log table by Table name.
  • Add the ID of the create log in the Row ID field.
  • Select Failed in the Log status field.
  • Select Inactive in the Status field.
  • Add the following text to the Log details: Flow log action(s):
  • Add the following code to the Log details, to show the error message from the try scope.
body('Filter_results_with_failed')[0]?['outputs']

Office 365: What’s new from Microsoft Ignite

During Microsoft Ignite many innovations that transform the workplace communications have been announced. Its impossible to name them all, so instead I will share with you my favorite innovations. Note that some of these features are already live, being rolled out or still being developed.

SharePoint app bar

The SharePoint app bar might even be my favorite announcement from Ignite. The app bar will be visible on every site on your intranet providing a consistent navigation experience. But it’s not only for navigation, that is just the beginning. The app bar also shows personal relevant sites, news feed and the files.
The SharePoint app bar will appear after you set a SharePoint site as a home site.

thumbnail image 1 of blog post titled 
	
	
	 
	
	
	
				
		
			
				
						
							Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation
							
						
					
			
		
	
			
	
	
	
	
	

	
	
	 
	
	
	
				
		
			
				
						
							Re: Introducing a SharePoint app bar that features global navigation

Home site in Teams

Your intranet (home site) is coming to Microsoft Teams. With the home site app for Microsoft Teams users gain direct access to the SharePoint Home site in Teams. The home site app in Teams gives your users global navigation across sites, communities, and teams; quick access to sites they use regularly; and a personalized news feed.

thumbnail image 1 captioned The home site app for Teams brings the power of your SharePoint-based intranet home site seamlessly into Microsoft Teams.

SharePoint: News boost

Microsoft listened to our request; we are getting the ability to boost news! With this feature you wil be able to boost the visibility of your important news articles. You can boost a news post until it has been read, for a set number of impressions, or until a given date.

thumbnail image 3 of blog post titled 
	
	
	 
	
	
	
				
		
			
				
						
							Innovations for workplace communications and employee engagement in Microsoft 365

SharePoint: news digest 

Keeping up with all the all the news and information can be harder than it seems. With the automatically generated news digest you will receive an email summary of the news articles you have missed. This will ensure that everybody will be well informed. The news digest can be customized with your organization’s branding.

thumbnail image 5 of blog post titled 
	
	
	 
	
	
	
				
		
			
				
						
							Innovations for workplace communications and employee engagement in Microsoft 365

Teams: Dynamic view

Dynamic view makes uses of AI to optimize shared content and video participant, it intelligently arranges the elements of your meeting for an optimal viewing experience. The viewing experience will change when people join, turn on video, start to speak or when starting to present. You will still be able change the layout based on your preference.

thumbnail image 1 of blog post titled 
	
	
	 
	
	
	
				
		
			
				
						
							What's New in Microsoft Teams | Microsoft Ignite 2021

SharePoint spaces 

SharePoint spaces is a very powerful new tool for 3D interactions. SharePoint spaces enables you to make immersive and engaging mixed reality experiences for 3D content, models and 360° imagery.

Office 365: What’s new?

The updates for Office 365 keep coming and coming, Microsoft is not sitting still! I am very excited for the following new features. Note that some of these features are already live, being rolled out or still being developed.

Microsoft Viva

Microsoft Viva is an employee experience platform, helping you create a work environment that puts people first, driving better business results.

It consists of Topics, Connections, Learning and Insights which are (partly) modules based on Microsoft Project Cortex technology. It brings together people, knowledge, e-learning using Microsoft Teams and SharePoint. And it also brings a Viva Insights app into Teams, utilizing data from Workplace Analytics.

Teams: Virtual Breakout Rooms

The virtual break out rooms are here! The meeting owner can create break out rooms to be used by the meeting participants for smaller group discussion. Participants can be assigned to a room and call them back to the larger group when the breakout is complete.

Teams: End-of-meeting notification

Microsoft Teams will notify the meeting participant when there is 5 minutes left in the scheduled meeting time. This will help users to be on time for the next meeting or shorting meetings that might drag on for too long.

Teams: Attendee Dashboard

Download the attendance list with a simple button.

Teams: Spotlight

It’s now easier to see if you are in the spotlight and to remove yourself from the spotlight.

SharePoint: 250GB file size support

The new upload limit for large files is now 250 GB for Teams, SharePoint, and OneDrive

SharePoint: Image Lightbox

Users can click on an image web part while in view mode, to see a larger version of the image.