• Power Pages Bing Address Validation (Autosuggest)

    Updated version below…

    Two things before we start.

    First, I am genuinely surprised that I couldn’t find a Power Pages article on this topic.

    I feel almost embarrassed posting this, as it seems like everyone else knows how to do it.

    Yet, I have a rule: if I struggle with something, there must be someone somewhere trying to build something similar. For the person crying for help somewhere in the world… here you go! I hope this helps you feel better 🙂

    Second, the functionality I am talking about won’t exist for long. But we all know how dev works. I promise you another one for the Azure Maps soon.

    Important announcement!

    Bing Maps for Enterprise is deprecated and will be retired. Enterprise account customers can continue to use Bing Maps for Enterprise services until June 30th, 2028. Free (Basic) account customers can continue to use Bing Maps for Enterprise services until June 30th, 2025. To avoid service disruptions, all implementations using Bing Maps for Enterprise REST APIs and SDKs will need to be updated to use Azure Maps by the retirement date that applies to your Bing Maps for Enterprise account type. For migration documentation, please see Bing Maps Migration Overview. For more details on the retirement, see the announcement on the Bing Maps Blog.

    And as always, WHY and WHY NOW.

    We are replacing Canvas App with the Power Pages website. The app had the autosuggest functionality implemented so we need it as well. For Canvas App we have Azure Maps validation OOTB:

    https://learn.microsoft.com/en-us/power-apps/maker/canvas-apps/geospatial-component-input-address

    OK. All formalities are finished. Let’s get to work!

    This is a great article here, basically, giving you a working HTML:

    https://learn.microsoft.com/en-us/bingmaps/v8-web-control/map-control-concepts/autosuggest-module-examples/filling-in-an-address-form-example

    You just need to get a Bing Maps key on a dev portal here:

    https://www.bingmapsportal.com/

    OK. We’ve got a working HTML with the address validation.

    But how do you say it in Power Pages?

    Let’s see what we need to do and what’s the difference. The original HTML is below for your reference.

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
        <meta charset="utf-8" />
    	<script type='text/javascript'>
        function GetMap() {
            Microsoft.Maps.loadModule('Microsoft.Maps.AutoSuggest', {
                callback: function () {
                    var manager = new Microsoft.Maps.AutosuggestManager({
                        placeSuggestions: false
                    });
                    manager.attachAutosuggest('#searchBox', '#searchBoxContainer', selectedSuggestion);
                },
                errorCallback: function(msg){
                    alert(msg);
                },
                credentials: 'Your Bing Maps Key' 
            });
        }
    
        function selectedSuggestion(result) {
            //Populate the address textbox values.
            document.getElementById('addressLineTbx').value = result.address.addressLine || '';
            document.getElementById('cityTbx').value = result.address.locality || '';
            document.getElementById('countyTbx').value = result.address.district || '';
            document.getElementById('stateTbx').value = result.address.adminDistrict || '';
            document.getElementById('postalCodeTbx').value = result.address.postalCode || '';
            document.getElementById('countryTbx').value = result.address.countryRegion || '';
        }
        </script>
        <style>
            #searchBox {
                width: 400px;
            }
            
            .addressForm {
                margin-top:10px;
                background-color: #008272;
                color: #fff;
                border-radius:10px;
                padding: 10px;
            }
    
            .addressForm input{
                width:265px;
            }
        </style>
        http://www.bing.com/api/maps/mapcontrol?callback=GetMap
    </head>
    <body>
        <div id='searchBoxContainer'>
            <input type='text' id='searchBox'/>
        </div>
    
        <table class="addressForm">
            <tr><td>Street Address:</td><td><input type="text" id="addressLineTbx"/></td></tr>
            <tr><td>City:</td><td><input type="text" id="cityTbx"/></td></tr>
            <tr><td>County:</td><td><input type="text" id="countyTbx"/></td></tr>
            <tr><td>State:</td><td><input type="text" id="stateTbx"/></td></tr>
            <tr><td>Zip Code:</td><td><input type="text" id="postalCodeTbx"/></td></tr>
            <tr><td>Country:</td><td><input type="text" id="countryTbx"/></td></tr>
        </table>
    </body>
    </html>

    First, we need to include this script below between the <head> tags :

    http://www.bing.com/api/maps/mapcontrol?callback=GetMap

    In our Power Pages Management app we find or create a content snippet “Head/Bottom”:

    Add the script link there.

    Next, what do we do with the rest of the script? It depends.

    If you want to add it to all pages on your website and nothing changes in terms of the control name you use across, you could technically keep it inside <head>.

    I will add it to the page that I know contains the form requiring address validation.

    Even before we touch the script. Let’s check we have all the fields required and list all the field names as we need them to modify the script.

    We need an Address Search field, for the POC I used an existing field, address1_name.

    And for the address fields, for my form, it will be:

    Street:address1_line1
    City:address1_city
    State:address1_stateorprovince
    Postal Code:address1_postalcode
    Country:address1_country

    OK. Let’s put the script on the page.

    HERE I PUT THE UPDATE>

    In the old version, I put the script on the web page. It’s better to put it on the form instead. I had to call GetMap() explicitly to get it working and now it works as designed. I had to delete some steps in the article to reflect the change.

    In the original HTML, we have a searchBox wrapped in the searchBoxContainer <div>.

    <div id='searchBoxContainer'>
            <input type='text' id='searchBox'/>
        </div>

    If you inspect the custom search address input you added to the form on the portal, it is also wrapped in <div> but it doesn’t have an ID.

    From the documentation here https://learn.microsoft.com/en-us/bingmaps/v8-web-control/modules/autosuggest-module/autosuggestmanager-class, the attachAutosuggest method requires the suggestedContainerId to be specified.

    NameReturn TypeDescription
    attachAutosuggest(suggestionBoxId: string, suggestionContainerId: string, selectedSuggestionCallback: function(result:SuggestionResult))Attaches the Autosuggest functionality to an input box.

    • suggestionBoxId – The HTML element identifier of the input box.
    • suggestionContainerId – The Id of container where suggestions will be displayed.
    • selectedSuggestionCallback – A reference to a callback function that will be called when a user selects a suggestion from the Autosuggest UI.

    This is how we add ID to the existing <div> container:

    $(document).ready(function() {    
          $("#address1_name").parent().attr("id", "searchBoxContainer");    
            
    });

    What else is left?

    We place two functions code, GetMap and selectSuggestion, outside $(document).ready:

    function GetMap() {           
                Microsoft.Maps.loadModule('Microsoft.Maps.AutoSuggest', {
                    callback: function () {
                        var manager = new Microsoft.Maps.AutosuggestManager({
                            placeSuggestions: false,
                            countryCode : 'AU'
                        });                   
                        manager.attachAutosuggest('#address1_name', '#searchBoxContainer', selectedSuggestion);
                      
                    },
                    errorCallback: function(msg){
                        alert(msg);
                    },
                    credentials: 'YOUR BING MAP KEY' 
                });        
            } 
        
            function selectedSuggestion(result) {            
                //Populate the address textbox values.            
                $("#address1_line1").val(result.address.addressLine || '');
                $("#address1_city").val(result.address.locality || '');           
                $("#address1_stateorprovince").val(result.address.adminDistrict || '');
                $("#address1_postalcode").val(result.address.postalCode || '');
                $("#address1_country").val(result.address.countryRegion || '');
            }

    we populate address fields from the result object.

    I used AutosuggestOptions Object https://learn.microsoft.com/en-us/bingmaps/v8-web-control/modules/autosuggest-module/autosuggestoptions-object countryCode property to limit suggestions to the Australian addresses.

    This is the complete JavaScript code I put on the page (address1_name in the code is your search address custom field):

    $(document).ready(function() {    
          $("#address1_name").parent().attr("id", "searchBoxContainer");        
    });
    
    function GetMap() {           
                Microsoft.Maps.loadModule('Microsoft.Maps.AutoSuggest', {
                    callback: function () {
                        var manager = new Microsoft.Maps.AutosuggestManager({
                            placeSuggestions: false,
                            countryCode : 'AU'
                        });                   
                        manager.attachAutosuggest('#address1_name', '#searchBoxContainer', selectedSuggestion);
                      
                    },
                    errorCallback: function(msg){
                        alert(msg);
                    },
                    credentials: 'YOUR BING MAP KEY' 
                });        
            } 
        
            function selectedSuggestion(result) {            
                //Populate the address textbox values.            
                $("#address1_line1").val(result.address.addressLine || '');
                $("#address1_city").val(result.address.locality || '');           
                $("#address1_stateorprovince").val(result.address.adminDistrict || '');
                $("#address1_postalcode").val(result.address.postalCode || '');
                $("#address1_country").val(result.address.countryRegion || '');
            }

    Finally, this is the screenshot of the working page below:

    Hope it helps.

  • “Hidden” SharePoint Flows (and more) on Power Platform. Do we clean it?

    Recently, we implemented the Inactivity process as part of the CoE Toolkit, which is a component of our general tenant hygiene strategy. If you’re unfamiliar with the concept, I suggest reading the article below: https://learn.microsoft.com/en-us/power-platform/guidance/coe/after-setup-tenant-hygiene

    Also, I suggest learning about the inactivity process setup: https://learn.microsoft.com/en-us/power-platform/guidance/coe/setup-archive-components

    If you don’t like reading, here’s a quick summary: it’s all about cleaning up your Power Platform assets that are not in use. Makers receive notifications about their Flows and apps that haven’t been modified or run, which are then selected for potential cleaning. ONLY APPROVED assets will be deleted.

    It’s a great set of tools to ensure your tenant remains clean. I could explain why, but let’s assume you already know this. (If you don’t, that’s OK—you can watch this video from one of the recent user group events where I cover this topic.)

    As I mentioned, we implemented this process for one of my customers.

    Overall, it was a success. People would either click the Approve button in the email or respond with comments like, ‘OH NO! DON’T DELETE IT! IT’S A MISSION-CRITICAL APP!’

    However, some people would email us asking, ‘What is this? I don’t know what you’re talking about.’

    One of those emails inspired me to dig deeper, and I’m sharing my findings with you.

    The Power Automate flow was named ‘Due date reminder,’ prefixed with a GUID. It didn’t sound like something a human would create—and indeed, it wasn’t.

    There are variations of this type of automation, but the one I’ll be discussing is part of the SharePoint list ‘Work Progress Tracker’ template.

    Some insightful reading for you here:

    https://support.microsoft.com/en-au/office/use-the-sharepoint-project-management-site-template-3d4ebdf3-03f7-485a-a16b-c4b4292d4b97

    and here:

    https://learn.microsoft.com/en-us/power-automate/create-sharepoint-reminder-flows

    Look what happens when I try to create a list using the template!

    SharePoint templates

    So far so good.

    Work Progress Tracker SharePoint template

    Now, this explicitly tells you that it will create a Power Automate Flow on your behalf.

    Set up Due Date reminder

    And here.

    Checking connections

    Then here as well.

    Alert that the Flow is added

    But people often don’t read messages. Maybe they do, but then forget about it afterward. Or they might just click through absentmindedly—we will never know.

    As a result of the above exercise, whether consciously or unconsciously, I triggered the creation of a Flow. The Flow in the solution below was created on my behalf:

    The solution with the reminder Flow

    Now, it will be picked up by the Inactivity process after some time to send me a reminder to clean up after myself. Will I remember creating it?

    Thinking emoji

    I can tell you that there are more Flows like this on the Default environment. I haven’t yet figured out how to address the existence of these ‘unconscious makers.’ We have excluded the ‘Due Date reminder’ Flows from the process until we decide on a course of action.

    As a Power Platform admin, I am curious—what else do I need to know? And guess what? There’s more to it!

    The Flow is created within a solution, and connection references are included as well. I don’t believe this is part of the existing CoE Toolkit cleaning routine.

    Components inside the “Due Date reminder” solution

    So we need to figure it all out: do we clean it? what do we clean? how do we clean?

    The winning question is:

    “How do you question someone about something they made without them knowing they were responsible for it?”

    (Sigh)

  • Power Pages: Migrate standard data model sites to enhanced data model – Part 2

    The previous article is here: https://msolenacrm.blog/2024/08/06/power-pages-migrate-standard-data-model-sites-to-enhanced-data-model-part-1/

    Let’s complete the upgrade!

    We are up to Step 4 now: https://learn.microsoft.com/en-us/power-pages/admin/migrate-enhanced-data-model#step-4-update-site-data-model-version-after-successful-data-migration

    Use the following command update site data model version:

    pac powerpages migrate-datamodel --webSiteId [WebSiteId-GUID] --updateDatamodelVersion --portalId [Portal-GUID]

    You are lucky if it works. It didn’t work for us, unfortunately.

    We received an error and from the log file, it was clear that it was looking for the website on the wrong environment. It was looking at the Default environment. Despite displaying us a message “Connecting to the environment A…”

    OK.

     pac auth list

    Index Active Kind      Name User                             Cloud  Type Environment           Environment Url

    [1]          UNIVERSAL      <User Name> <Environemnet> <URL1>

    [2]   *      UNIVERSAL      <User Name> <Correct Environment>    <URL2>

    Now we used the following command to select the correct environment, which is the index 2 item.

    pac auth select --index 2

    New default profile:

        * UNIVERSAL                                : <User Name>         Public <Correct environment URL>

    And then we run the command:

    pac powerpages migrate-datamodel --webSiteId [WebSiteId-GUID] --updateDatamodelVersion --portalId [Portal-GUID]

    This time it worked as expected.

    Current migration status is : CompletedSite is updated to enhanced data model successfully.Site is ready to browse now.
    

  • Power Pages: Migrate standard data model sites to enhanced data model – Part 1

    The standard data model was built by using custom tables, and it was optimized for the configuration of each website component that is stored as a record in a dedicated table in Microsoft Dataverse. The standard model requires more time to load the different solutions, tables, and metadata when a new site is provisioned. Updates to website tables in the standard model require manual and time-consuming application of package updates.

    The enhanced data model is a combination of system tables, nonconfiguration tables, and virtual tables.

    The enhanced data model for Power Pages provides the following benefits:

    • Website provisioning is faster.
    • Design studio experiences are faster.
    • Website configurations can be contained in solutions to provide smoother application lifecycle management (ALM) experiences.
    • Updates of Power Pages enhancements and bug fixes are improved.

    Read more about the enhanced data model here: https://learn.microsoft.com/en-us/power-pages/admin/enhanced-data-model

    We are going through the trial migration at the moment following the steps in the article below: https://learn.microsoft.com/en-us/power-pages/admin/migrate-enhanced-data-model

    As we encountered some issues and were able to overcome them and move forward, I would like to share our ongoing journey with the community. I hope it saves some of your time.

    The section in the article refers to the original article: https://learn.microsoft.com/en-us/power-pages/admin/migrate-enhanced-data-model. Open it in a separate tab of your browser.

    Prerequisites

    Check all the packages, and versions, check carefully as we checked then failed then checked again then upgraded to the correct version…check it carefully.

    Step 1. Download and check customization for existing standard site metadata.

    Use the command below to run the customization report:

    pac powerpages migrate-datamodel --webSiteId [WebSiteId-GUID] --siteCustomizationReportPath [PATH]

    To find the results look in the My Documents folder, it doesn’t respect the path you specified in the siteCustomizationReportPath parameter.

    I found the report results very limited. We created a report of our own using the tips from the section “Considerations for site customization when migrating sites from standard to enhanced data model“: https://learn.microsoft.com/en-us/power-pages/admin/migrate-enhanced-data-model#considerations-for-site-customization-when-migrating-sites-from-standard-to-enhanced-data-model For this, we went through the source code of the website listing all the components with potential after-migration issues. It will help us to test the website after the migration.

    There are five types of site customizations on adx metadata tables:

    All customization related fixes will be done after migration to the enhanced data model.

    Step 2. Migrate the site data from standard to enhanced data model

    Do it before, not after running the next command. Go to PPAC following the path in the screenshot below to unblock the js extension.

    I know you think you unblocked it already by doing it here:

    But it is not the same!

    Trust me, you don’t want to waste your time like we did.

    Now just run the command:

    pac powerpages migrate-datamodel --webSiteId [WebSiteId-GUID] –-mode [type-of-data]

    Currently, we are up to Step 4. I will let you know how we go in the next article.

    Two more things.

    Thing 1. If it didn’t go as planned…

    You can re-run the command but delete the migration tracker first:

    You don’t have to delete a newly created website, it will self-update.

    Thing 2. We had some local issues with one of the laptops.

    We had some weird ArgumentOutOfRange exception. Try a different laptop if you get something similar that doesn’t make sense.

    This is it for now. I will keep you updated 🙂 Hope it helps.

  • In my Power Platform dev life I use custom connectors a lot. One of the latest use cases is limiting a standard Microsoft Entra ID connector to meet the least privilege principle.

    Custom connectors could be very useful. However, there are some tricks and catches in the implementation and the deployment.

    A new post from Carina M. Claesson is the first one in the series of articles about a Custom Connector.

    Sharing it here with a big ThankYou to the author.