Jump to content

Resco Inspections on Power Platform: Difference between revisions

From Resco's Wiki
Line 123: Line 123:
[[File:Set-web-API-access-in-site-settings.png|600px]]
[[File:Set-web-API-access-in-site-settings.png|600px]]


<!--
=== Work with questionnaires ===


=== Create a Power Page ===
;Initialize the player component
:Call the static "init" method a provide it the iframe from your page.


# Go to https://make.powerpages.microsoft.com/ (Power Pages design studio).
;Open a questionnaire
# Ensure you are in the environment with the Woodford solution installed.
:Use the "openQuestionnaire" method to display the questionnaire with a specific ID.
# Create a new power page or edit an existing one.
:<syntaxhighlight lang="js">
# Go to '''Pages''' and select the page where you want to add the Questionnaire Player.
# Add the following script to the page:<br><code><script type="text/javascript" src="./qplayerpp.bundle.js"></script></code>
# Insert an iframe to the page.
# Initialize the player component by passing it the iframe from your page. Then, you can call the openQuestionnaire method to show the questionnaire with a specific id.<br><syntaxhighlight lang="js">
<script type="text/javascript">
<script type="text/javascript">
   $(document).ready(function() {
   $(document).ready(function() {
     InspectionsPlayerPP.init(function(player) {
     InspectionsPlayer.init(function(player) {
       player.show();
       player.show();
       player.openQuestionnaire("83c7c901-8de2-ed11-a7c7-6045bd8d9620", {id: "36be2493-3bab-eb11-8236-000d3a49e729", entityName: "account"});
       player.openQuestionnaire("83c7c901-8de2-ed11-a7c7-6045bd8d9620");
     }, document.getElementById('qplayerIFrame'));
     }, document.getElementById('qplayerIFrame'));
   });
   });
</script></syntaxhighlight>This opens the player and shows the questionnaire with id "18497562-c4d2-ed11-a7c7-6045bd8d9f24".
</script></syntaxhighlight>
:This opens the player and shows the questionnaire with id "83c7c901-8de2-ed11-a7c7-6045bd8d9620".


=== List of methods ===
=== List of methods ===


'''static init(ready: (player) -> void, iFrame: HTMLIFrameElement)'''
Here is a list of methods provided by the questionnaire player controller for your reference:
:This method initializes the questionnaire player. It is mandatory to call this method prior to using the questionnaire player. It initializes connection between Power Page controller and questionnaire player itself.


;show()
==== [static] init(ready: (player) -> void, iFrame: HTMLIFrameElement) ====
:Displays the iframe in which the player is loaded


;hide()
Description: This method is essential to initialize the questionnaire player. It establishes the connection between the Power Page controller and the questionnaire player. The callback includes the player object, which provides control over the questionnaire player. This object can be used to manage and interact with the questionnaire player's functionality.
:Hides the iframe in which the player is loaded
Parameters:
* ready: A callback function that gets executed once the player is ready.
* iFrame: The HTMLIFrameElement associated with the player.


'''openQuestionnaire(id: string, regarding?: {id: string, entityName: string})'''
==== show() ====
:This method loads and displays the questionnaire with id passed as parameter. The format of the id is GUID.
:You can optionally pass regarding parameter. The parameter is a json object with two properties:
:* id – id of the regarding object
:* entityName – entity name of the regarding object
: Example: {id: "36be2493-3bab-eb11-8236-000d3a49e729", entityName: "account"}


'''saveQuestionnaire(close: boolean)'''
Description: Displays the iframe containing the questionnaire player.
:Saves the opened questionnaire and closes the player if the close parameter is set to true.


'''clearQuestionnaire(force: boolean)'''
==== hide() ====
:Closes the displayed questionnaire. If the force parameter is set to true, the player will not prompt the user to save the data if the questionnaire was modified.
 
Description: Hides the iframe containing the questionnaire player.
 
==== openQuestionnaire(id: string, regarding?: {id: string, entityName: string}) ====
 
Description: Loads and displays the questionnaire with the specified ID. The ID should be in GUID format.
 
You can associate a questionnaire with a specific record by passing a JSON object with two properties to the openQuestionnaire method. This allows you to establish a connection between the questionnaire and a particular record.
   
Parameters:
* id: The ID of the questionnaire to be displayed.
* regarding [optional]: A JSON object with two properties:
:* id: The ID of the regarding object.
:* entityName: The entity name of the regarding object
 
==== saveQuestionnaire(close: boolean) ====
 
Description: This method saves the currently opened questionnaire. If the close parameter is set to true, the player will also be closed.
 
Parameters:
* close: A boolean parameter that, when set to true, closes the player after saving the questionnaire.
 
==== clearQuestionnaire(force: boolean) ====
 
Description: Closes the displayed questionnaire. If the force parameter is set to true, the player will not prompt the user to save data if the questionnaire was modified.  
   
Parameters:
* force: A boolean parameter that, when set to true, closes the questionnaire without prompting for unsaved changes.
 
==== executeCommand(commandName: string) ====
 
Description: Executes a system or custom command on the questionnaire. You should pass the name of the command to be executed as a parameter.
Parameters:
* commandName: The name of the command to execute.
 
==== onCommandsUpdated: (commands: QuestionCommand[]) => void ====
 
Description: This callback is triggered when a set of commands associated with the questionnaire is updated. You can use this callback to retrieve the list of commands and their status (enabled/disabled).
 
Parameters:
* commands: An array of command objects, each containing properties such as name (string), label (string), and isEnabled (boolean).
 
==== onShowDetails: (entity: Reference, relationship: object) => void ====
 
Description: In cases where a questionnaire contains a lookup question, clicking on the lookup should display details of the related record. However, in Power Pages, the designer has control over what forms or pages to use. Therefore, this callback provides information about the record clicked on, allowing the designer to decide how to display it.
 
Parameters:
* entity: An object containing fields entityName (string), id (string), and displayName (string), representing the entity related to the clicked record.
* relationship: The relationship object of the questionnaire
 
==== onQuestionnaireClosed: () => void ====
 
Description: This callback is invoked when the questionnaire is closed
 
==== onQuestionnaireSaved: (id: string, status: "active" | "completed" | "cancelled") => void ====
 
Description: This callback is invoked when the questionnaire is saved, completed or cancelled
   
Parameters:
* id: The id of the saved questionnaire
* status: The string representing the status of the saved questionnaire
 
=== Appendix A: Sample Power Page code ===
 
<syntaxhighlight lang="js">
<p>The Knowledge Base contains numerous support references, created by our support professionals who have resolved issues for our customers. It is constantly updated, expanded, and refined to ensure that you have access to the very latest information.</p>
 
<div class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;">
  <div class="container" style="padding: 0px; display: flex; flex-wrap: wrap;">
    <div class="col-md-12 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 300px;">
    </div>
  </div>
</div>
 
<div style="display: flex; flex-direction: row;">
  <div style="flex: 2 2 auto">
    <h2>Inspections Player</h2>
    <iframe id="qplayerIFrame" frameborder="0" style="display: none; width: 800px; height: 800px; max-width: 100%;"></iframe>
  </div>
  <div style="flex: 1 1 auto; background-color: #ddd; padding: 0px 10px">
    <h2>Commands</h2>   
    <div class="qpCommands"></div>
    <h2>Questionnaires</h2>
    <span id="myspan1" style="color: blue; text-decoration: underline; cursor: pointer">Request Visit</span><br />
    <span id="myspan2" style="color: blue; text-decoration: underline; cursor: pointer">Command Test</span><br />
  </div>
  <span id="output" style="color: #444"></span>
</div>
 
<script type="text/javascript" src="/_webresource/resco_MobileCRM/PowerPages/inspectionsPlayer.js"></script>
 
<script type="text/javascript">
  var $qpCommandsDiv = $(".qpCommands");
  function commandsUpdated(commands) {
    for (var i = 0; i < commands.length; i++) {
      var $qpCommand = $("#" + commands[i].name, $qpCommandsDiv);
      if ($qpCommand.length === 0) {
        $qpCommand = $("<div id=" + commands[i].name + " style='color: blue; text-decoration: underline; cursor: pointer'></div>");
        $qpCommandsDiv.append($qpCommand);
        $qpCommand.on("click", function(command) {
            alert(command.name);
            this.executeCommand(command.name);
        }.bind(this, commands[i]));
      }
      $qpCommand.html(commands[i].label);
      if (commands[i].isEnabled) {
        $qpCommand.css({"cursor": "pointer", "text-decoration": "underline", "color": "blue"});
      } else {
        $qpCommand.css({"cursor": "inherit", "text-decoration": "none", "color": "silver"});
      }
    }
  }
 
  function showDetails(entity, relationship) {
    alert("Show details of: " + JSON.stringify(entity));
  }
 
  function questionnaireClosed() {
    $qpCommandsDiv.empty();
  }
 
  function questionnaireSaved(id, status) {
    alert("questionnaire id: '" + id + "', status: '" + status + "'");
  } 
 
  $(document).ready(function() {
    InspectionsPlayer.init(function(player) {
      player.onCommandsUpdated = commandsUpdated.bind(player);
      player.onShowDetails = showDetails;
      player.onQuestionnaireClosed = questionnaireClosed;
      player.onQuestionnaireSaved = questionnaireSaved;
      player.show();
      $("#myspan1").click(function() {
        var $qpCommandsDiv = $(".qpCommands");
        $qpCommandsDiv.empty();
        player.openQuestionnaire("83c7c901-8de2-ed11-a7c7-6045bd8d9620", {id: "36be2493-3bab-eb11-8236-000d3a49e729", entityName: "account"});
      });
      $("#myspan2").click(function() {
        var $qpCommandsDiv = $(".qpCommands");
        $qpCommandsDiv.empty();
        player.openQuestionnaire("96553634-8de2-ed11-a7c7-6045bd8d9621");
      });
    }, document.getElementById('qplayerIFrame'));
  }); 
</script>
</syntaxhighlight>


'''executeCommand(commandName: string)'''
:Executes the system or custom command on the questionnaire. The name of the command to execute is passed as a parameter.


'''onCommandsUpdated: (commands: QuestionCommand[]) => void'''
:Callback that is called when a set of commands of the questionnaires is updated. You can use this callback to get the list and status (enabled/disabled) of the commands.
:The list of command objects is passed to this callback. Each object contains properties: name: string, label: string, isEnabled: boolean


'''onShowDetails: (entity: Reference) => void'''
:Questionnaire might contain a lookup question. If user clicks on the lookup, the app should display the details of the record stored in that question. In mobile crm or in power apps the appropriate form for the entity is opened and the record is displayed in that form.
:In Power Pages we do not have control over what forms or pages the designer of the site has. So, we at least pass him info about the record that was clicked on and let the designer decide what to display.
:The parameter '''entity''' contains fields: '''entityName: string, id: string, displayName: string'''


'''onQuestionnaireClosed: (saved: boolean) => void'''
:Called when the questionnaire was closed and/or saved








-->





Revision as of 12:09, 11 September 2023

Warning Work in progress! We are in the process of updating the information on this page. Subject to change.

Traditionally, end users consume questionnaires in Resco mobile apps. Since Release 16.1 (summer 2023), questionnaires can also be used in various components of the Microsoft Power Platform: Power Apps and Power Pages.

As your backend server, use Microsoft Dataverse - with or without extra Dynamics features.

Prerequisites

Here's a high-level overview of steps to prepare your environment:

  1. Go to https://make.powerapps.com/.
  2. Install the Woodford managed solution. (How?)
  3. Use the Questionnaire Designer to create and publish at least one questionnaire.
Preview Inspections on Power Platform are still in preview. In step 2, don't install the production version of the Woodford solution released earlier this year. Instead, see the Preview page for a newer version.

Why Woodford

In this scenario, the Woodford tool is not used. So why is the Woodford solution needed? It includes several other components necessary for inspections:

  • Custom tables, e.g., questionnaire
  • Questionnaire Designer, the backend tool for designing and managing questionnaire templates
  • Several standard views for the questionnaire table, as well as a custom form - the Questionnaire Player web resource. These can be used on the Power Platform.

Power Apps

The Questionnaire table can be easily integrated into your Power Apps with only a few clicks. Treat Questionnaire just like a standard Dataverse table, with the Questionnaire player replacing the standard table form.

Power Pages

Microsoft Power Pages is a platform for creating, hosting, and managing websites. The following example explains how to include the Questionnaire Player on a webpage in a few steps:

  1. Prepare a special app project in Woodford.
  2. Create a Power Page and add the Questionnaire Player
  3. Set up permissions for tables necessary for the Questionnaire Player
  4. Grant web API access for the same tables

Create a dedicated app project

For version 1.0 of the Power Pages Questionnaire Player, it is essential to have a mobile project in Woodford that is configured to work with Inspections and is named "PPInspectionsPlayer". The simplest way to achieve this is by cloning the predefined Inspections project. Publish the app project afterwards.

Create a Power Page

  1. Go to https://make.powerpages.microsoft.com/ (Power Pages design studio).
  2. Ensure you are in the environment with Resco Woodford solution installed.
  3. Create a new power page or edit an existing one.
  4. Go to Pages and select the page where you want to add the Questionnaire Player.
  5. Insert an iframe to the page, then switch to Edit code and use the following code to define the iframe content:
    <iframe frameborder="0" id="qplayerIFrame" data-ppid="1565191e-bc3e-7bad-6805-6ffd20a70b42" style="width: 800px; height: 800px; max-width: 100%;"></iframe>
  6. Include the following script within the page:
    <script type="text/javascript" src="/_webresource/resco_MobileCRM/PowerPages/inspectionsPlayer.js" data-ppid="3699bcda-72e0-21e0-94cb-71dee05f0691"></script>
  7. Save the modified code, return to Power Page design studio, and click Sync to retrieve the latest code.

Set up table permissions

Add the necessary permissions for the following tables:

  • resco_mobileproject – Read access
  • resco_mobiledata – Read access
  • resco_questionnaire – Read, Write, Update, Delete, Append, Append to
  • resco_questiongroup – Read, Write, Update, Delete, Append, Append to
  • resco_question – Read, Write, Update, Delete, Append, Append to
  • annotation – Read, Write, Update, Delete, Append, Append to
  • systemuser – Read, Append, Append to
  • webresource – Read, Append, Append to
  • Any entity accessible from your questionnaires (e.g., account, contact…)

You can decide what kind of access you want to grant users. Your web can be accessed by anonymous users or authenticated users. The access type might be global or parent, account, or contact. You can learn more about the security model of power pages in official Microsoft documentation. In this document, we describe how to grant access to the mentioned tables for anonymous users.

While editing your site, go to Set up > Table permissions. For each table, repeat the following procedure:

  1. Click +New.
  2. Enter the Name of your permission, Website, and the Table Name.
  3. Set Access Type to "Global".
  4. Check the required privileges, for example, Read.
  5. Click Add roles and check role(s) for which the permission is created. In our case, it is Anonymous Users.
  6. Save all changes.

Set up web API access

Questionnaire Player uses Microsoft Web API service to communicate with the underlying Dataverse organization. You must set the Web API access to the same tables as in the previous step.

  1. While editing your site, click the ellipsis button in the left pane (just under Set Up) and select Portal Management.
  2. Select Website > Site Settings from the menu.
  3. For each table, create two new site settings:
    • Enable the web API for this table.
    • Set up fields that should be available via web API (or use asterisk for all).

Work with questionnaires

Initialize the player component
Call the static "init" method a provide it the iframe from your page.
Open a questionnaire
Use the "openQuestionnaire" method to display the questionnaire with a specific ID.
<script type="text/javascript">
  $(document).ready(function() {
    InspectionsPlayer.init(function(player) {
      player.show();
      player.openQuestionnaire("83c7c901-8de2-ed11-a7c7-6045bd8d9620");
    }, document.getElementById('qplayerIFrame'));
  });
</script>
This opens the player and shows the questionnaire with id "83c7c901-8de2-ed11-a7c7-6045bd8d9620".

List of methods

Here is a list of methods provided by the questionnaire player controller for your reference:

[static] init(ready: (player) -> void, iFrame: HTMLIFrameElement)

Description: This method is essential to initialize the questionnaire player. It establishes the connection between the Power Page controller and the questionnaire player. The callback includes the player object, which provides control over the questionnaire player. This object can be used to manage and interact with the questionnaire player's functionality.

Parameters:

  • ready: A callback function that gets executed once the player is ready.
  • iFrame: The HTMLIFrameElement associated with the player.

show()

Description: Displays the iframe containing the questionnaire player.

hide()

Description: Hides the iframe containing the questionnaire player.

openQuestionnaire(id: string, regarding?: {id: string, entityName: string})

Description: Loads and displays the questionnaire with the specified ID. The ID should be in GUID format.

You can associate a questionnaire with a specific record by passing a JSON object with two properties to the openQuestionnaire method. This allows you to establish a connection between the questionnaire and a particular record.

Parameters:

  • id: The ID of the questionnaire to be displayed.
  • regarding [optional]: A JSON object with two properties:
  • id: The ID of the regarding object.
  • entityName: The entity name of the regarding object

saveQuestionnaire(close: boolean)

Description: This method saves the currently opened questionnaire. If the close parameter is set to true, the player will also be closed.

Parameters:

  • close: A boolean parameter that, when set to true, closes the player after saving the questionnaire.

clearQuestionnaire(force: boolean)

Description: Closes the displayed questionnaire. If the force parameter is set to true, the player will not prompt the user to save data if the questionnaire was modified.

Parameters:

  • force: A boolean parameter that, when set to true, closes the questionnaire without prompting for unsaved changes.

executeCommand(commandName: string)

Description: Executes a system or custom command on the questionnaire. You should pass the name of the command to be executed as a parameter.

Parameters:

  • commandName: The name of the command to execute.

onCommandsUpdated: (commands: QuestionCommand[]) => void

Description: This callback is triggered when a set of commands associated with the questionnaire is updated. You can use this callback to retrieve the list of commands and their status (enabled/disabled).

Parameters:

  • commands: An array of command objects, each containing properties such as name (string), label (string), and isEnabled (boolean).

onShowDetails: (entity: Reference, relationship: object) => void

Description: In cases where a questionnaire contains a lookup question, clicking on the lookup should display details of the related record. However, in Power Pages, the designer has control over what forms or pages to use. Therefore, this callback provides information about the record clicked on, allowing the designer to decide how to display it.

Parameters:

  • entity: An object containing fields entityName (string), id (string), and displayName (string), representing the entity related to the clicked record.
  • relationship: The relationship object of the questionnaire

onQuestionnaireClosed: () => void

Description: This callback is invoked when the questionnaire is closed

onQuestionnaireSaved: (id: string, status: "active" | "completed" | "cancelled") => void

Description: This callback is invoked when the questionnaire is saved, completed or cancelled

Parameters:

  • id: The id of the saved questionnaire
  • status: The string representing the status of the saved questionnaire

Appendix A: Sample Power Page code

<p>The Knowledge Base contains numerous support references, created by our support professionals who have resolved issues for our customers. It is constantly updated, expanded, and refined to ensure that you have access to the very latest information.</p> 

<div class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;"> 
  <div class="container" style="padding: 0px; display: flex; flex-wrap: wrap;">
    <div class="col-md-12 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 300px;">
    </div>
  </div> 
</div>

<div style="display: flex; flex-direction: row;"> 
  <div style="flex: 2 2 auto"> 
    <h2>Inspections Player</h2> 
    <iframe id="qplayerIFrame" frameborder="0" style="display: none; width: 800px; height: 800px; max-width: 100%;"></iframe> 
  </div> 
  <div style="flex: 1 1 auto; background-color: #ddd; padding: 0px 10px"> 
    <h2>Commands</h2>     
    <div class="qpCommands"></div> 
    <h2>Questionnaires</h2> 
    <span id="myspan1" style="color: blue; text-decoration: underline; cursor: pointer">Request Visit</span><br /> 
    <span id="myspan2" style="color: blue; text-decoration: underline; cursor: pointer">Command Test</span><br /> 
  </div> 
  <span id="output" style="color: #444"></span> 
</div> 

<script type="text/javascript" src="/_webresource/resco_MobileCRM/PowerPages/inspectionsPlayer.js"></script> 

<script type="text/javascript"> 
  var $qpCommandsDiv = $(".qpCommands"); 
  function commandsUpdated(commands) { 
    for (var i = 0; i < commands.length; i++) { 
      var $qpCommand = $("#" + commands[i].name, $qpCommandsDiv); 
      if ($qpCommand.length === 0) { 
        $qpCommand = $("<div id=" + commands[i].name + " style='color: blue; text-decoration: underline; cursor: pointer'></div>"); 
        $qpCommandsDiv.append($qpCommand); 
        $qpCommand.on("click", function(command) { 
            alert(command.name); 
            this.executeCommand(command.name); 
        }.bind(this, commands[i])); 
      } 
      $qpCommand.html(commands[i].label); 
      if (commands[i].isEnabled) { 
        $qpCommand.css({"cursor": "pointer", "text-decoration": "underline", "color": "blue"}); 
      } else { 
        $qpCommand.css({"cursor": "inherit", "text-decoration": "none", "color": "silver"}); 
      } 
    } 
  } 

  function showDetails(entity, relationship) { 
    alert("Show details of: " + JSON.stringify(entity)); 
  } 

  function questionnaireClosed() { 
    $qpCommandsDiv.empty(); 
  } 

  function questionnaireSaved(id, status) { 
    alert("questionnaire id: '" + id + "', status: '" + status + "'"); 
  }   

  $(document).ready(function() { 
    InspectionsPlayer.init(function(player) { 
      player.onCommandsUpdated = commandsUpdated.bind(player); 
      player.onShowDetails = showDetails; 
      player.onQuestionnaireClosed = questionnaireClosed; 
      player.onQuestionnaireSaved = questionnaireSaved; 
      player.show(); 
      $("#myspan1").click(function() { 
        var $qpCommandsDiv = $(".qpCommands"); 
        $qpCommandsDiv.empty(); 
        player.openQuestionnaire("83c7c901-8de2-ed11-a7c7-6045bd8d9620", {id: "36be2493-3bab-eb11-8236-000d3a49e729", entityName: "account"}); 
      }); 
      $("#myspan2").click(function() { 
        var $qpCommandsDiv = $(".qpCommands"); 
        $qpCommandsDiv.empty(); 
        player.openQuestionnaire("96553634-8de2-ed11-a7c7-6045bd8d9621"); 
      }); 
    }, document.getElementById('qplayerIFrame')); 
  });  
</script>






{{#CI form: title = Was this information helpful? How can we improve? | type = inputs | [textarea] }}