Redirecting SharePoint list's NewFormUrl and EditFormUrl to Power Apps

In this scenario, we have a Power Apps app that takes a query param() “AssetID” and determines whether the app should create a new item or update an existing item.

We want to change the SharePoint default New or Edit items on a modern SharePoint list to go to our App. The app is a landscape canvas application and not an integrated portrait application (so we can’t use SharePoint form setting to switch this).

Because SharePoint’s New and Edit forms must be server relative pages, we can create a redirect page.

Steps

  1. Use Param in Power Apps

  2. Create a simple redirect page

  3. Change SharePoint’s NewFormUrl and EditFormUrl to this redirect page

  4. Test: Redirect to PowerApps

  5. References

Use Param in Power Apps

https://powerapps.microsoft.com/en-us/blog/powerapps-deep-linking/

Create a simple redirect page

Create a redirect ASPX (HTML) page and upload it into the SitePage library. I saved this as "AssetEditForm.aspx"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%@ Page Language="C#" %>
<%@ Register tagprefix="SharePoint" namespace="Microsoft.SharePoint.WebControls" assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta name="WebPartPageExpansion" content="full" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Asset Form</title>
<meta http-equiv="X-UA-Compatible" content="IE=10" />
<SharePoint:CssRegistration Name="default" runat="server"/>
<script type="text/javascript">

function getQueryVariable(variable) {
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (decodeURIComponent(pair[0]) == variable) {
            return decodeURIComponent(pair[1]);
        }
    }
//    console.log('Query variable %s not found', variable);
}
var play = "https://apps.powerapps.com/play/e3fdef18-2be5-4d25-ba04-3edf6411c0aa?tenantId=afa78dff-85bb-46f3-b036-b43bcf79c497";
var id = getQueryVariable("ID");
if (id) {
    window.location.replace(play+"&AssetID="+id);
}
else {
    window.location.replace(play);
}
</script>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>


Change SharePoint’s NewFormUrl and EditFormUrl with PnP-PowerShell

Connect-PnPOnline -url https://johnliu365.sharepoint.com/
$list = Get-PnPList "Asset"
$list.ContentTypes[0]

# assuming list content type 0 == Item
# otherwise, you need to set this on the correct list content type

$list.ContentTypes[0].EditFormUrl = "SitePages/AssetEditForm.aspx"
$list.ContentTypes[0].NewFormUrl = "SitePages/AssetEditForm.aspx"
$list.ContentTypes[0].Update($false)

# no update child content types
# you may have to set $true if you have content types in your list

$context = Get-PnPContext
$context.ExecuteQuery()


Redirecting Magic

This will change the “New”, “Edit” behaviour on the SharePoint modern or classic list. Also, this will work in the edit menu. So if you want to redirect your users to the new Power Apps experience, this is one way to change the link everywhere.

Reference Reading

Please also read April Dunnam’s two part series on how to customize SharePoint via modern ListView JSON

A Power User friendly method of connecting hundreds of sites, list and libraries to a single Flow

Photo by Sebastian Boring on Unsplash

Photo by Sebastian Boring on Unsplash

We need to get this disclaimer out of the way first - this approach uses SharePoint Designer workflow as a glue - the main reason for this is because SharePoint Designer workflows (being old generation technology) can be deployed across site collections via PowerShell scripting without too much trouble, as long as they remain small and don’t need regular maintenance.

This approach isn’t necessary for deploying a Flow to a dozen sites. But if we are talking potentially hundreds of project sites. Those become scenarios that aren’t currently covered well, and would need workarounds such as this one.

Let me know what you think in the comments.

The Problem

There are many ways we can view this problem of “deploying Flows across many lists, libraries, sites and site collections”.

This problem has several issues we may not need to consider:

  • First, we have Export and Import, as well as Send Flow as a Copy. These are simple, manual steps. That can’t be automated.

  • But at some point - we will hit the 600 Flow limit (this isn’t per environment, this is per account)

  • We will need to reconfigure URLs specific to each Flow.

  • As these copied Flows are customized from the master version which makes updating and maintaining them very tricky.

  • When redeploy a Flow - existing connections must be maintained.

  • When deploying Flow to a new site - new connections must be configured (or existing connections re-used).

  • If we are ever in a situation where we need to deploy a new update an existing Flow but now with additional new Connections. God have mercy.

  • If we think perhaps just delete existing Flows and re-deploy as new, we’d kill any existing runs.

So there are alternate approaches as well… what if we just don’t deploy hundreds of copies of the same Flow?


A new solution?

The method in this blog post is a different idea - we will borrow SharePoint Designer workflow’s various tools for deploying cross site collections and have it invoke the same Flow to perform the main task.

This was posted as #FlowNinja hack 91

https://twitter.com/johnnliu/status/1121055884749053953


Let’s begin

Warning: this hack involves the use of SharePoint Designer. Now before you all run off screaming, there's a good reason why we want to try this method: mass deployments. So here we go

1. Make a Request trigger Flow. With a simple compose.
2. make SPd 2013 reusable workflow

3. first action - add SPd HTTP web request to the Flow request trigger
4. need to configure both request body and request header

5. If we don't blank out authorization this happens DirectApiAuthorizationRequired: The request must be authenticated only by Shared Access scheme

Add SPd Workflow to a Library and a List

6. now publish and add this to a List and a Document library
7. upload a file see SPd workflow trigger
8. see HTTP Request flow trigger

The Test is successful. Next copy the JSON we received in Flow

9. add Request schema from the previous successful run
10. change Compose to match request body

11. add a new item to a sharepoint list (that has the same SPd workflow attached)
12. the same Flow re-triggers.

Take a breather, have some tea

13. at this point, there's several more things to do - consider whether we want excessive logging. SPd workflow is simple - once it is working there's not a lot more to tweak, perhaps we want more context values. While still within this site collection, republish will update it.

Package the SPd Workflow for cross site collection

Let's package this workflow for cross site collection warp jump.

14 remove workflow association to list Achievements - because we don't have that list everywhere, only Documents. 15 export WSP to desktop

16 go to new site collection - this can be SharePoint or group sites or project sites.
17 site settings - upload solution to gallery - activate solution
18 site settings - site features -activate feature (this associates with Documents)

19 test with upload new file in new site collection
20 flow triggers, context of Flow is new site url.

21. note there was an error with the SPd workflow after it called HTTP - because I didn’t activate the SharePoint Workflow feature, Workflow History list isn’t available in the new site collection.

Consider Step #13 and whether you’d want to delete all the logging steps before exporting Flow


21. now we are in Flow. Re-fetch the item and go crazy.

Yes, in Step #4 we deliberately chose the context values that will allow us to re-configure Flow to pick up the item

Conclusion

Con:

  • The pain of writing a SharePoint Designer workflow again

  • Difficult to update deployed SPd workflows - but the one we have here is very simple (two build dictionary and a HTTP request)

Pros:

  • A simple lightweight SharePoint Designer workflow can be deployed across many site collections via PnP-PowerShell, or as part of PnP-Provisioning.

    Activate the solution, and then activate the feature (this will create the workflow associations)

  • All the events from associated SharePoint libraries will call a single Microsoft Flow - this is where we can customize to our heart’s content.

I don’t think this approach should be written off - it should be evaluated and may suit your situation well. I also think in a possible future when cross-site Flow deployments can be done easier - we may have different Flow triggers invoking our one Flow. So there exists an upgrade path forward.

Clearing SPD Cache from SharePoint Designer

Our good old friend SharePoint Designer is old, but I still find it useful for editing JavaScript files in development for quick tests.

Because SharePoint Designer caches what it thinks the site's contents look like, sometimes, this cache gets out of date with reality and you will see issues when SPD thinks you have some files checked out, but you don't.

And in the past, I would go and Bing and search "clear SharePoint Designer cache" and this will net you:

So there's no shortage of finding the command to clear the folder.

Until Today

I came across this part of the SPD UI by accident.

  • Select Home in the current site. 
  • Go to Site Options.
  • Select Advanced Tab
  • Click Delete Files (temporary files)

You will see that any currently Opened Folders views will be empty. 

As the cache for the site has been dumped.  You need to refresh from the Ribbon Bar and you'll see the status of the items has updated correctly.

SharePoint Designer 2013

The same UI exists for SharePoint Designer 2013 as well.

No more command line, perhaps.

Let me know in the comments if this was something you've ALWAYS known about, or whether it also made you feel foolish that you've been using the command-line or deleting cryptic user profile temporary folders for years, literally.  When there's a button that does this for me.

 

Feb 2015 CU SP2013: Workflow CurrentSiteUrl is fixed

 

http://support.microsoft.com/KB/2920804

Assume that you create a SharePoint 2013 workflow by using SharePoint Designer 2013, and that the workflow contains an action that retrieves data from the "Current Site URL" field in the "Workflow Context" data source. When you run the workflow, the value retrieved from the "Current Site URL" field is missing a forward slash ("/") at the end of the URL.

 

A few months ago, Microsoft released an update to SharePoint 2013, and it broke many SharePoint Designer Workflows.

If you use the new capabilities in SharePoint 2013 to call REST services, it is very common to concatenate the URL using a combination of the Current Site Url as well as the api/ path.

"http://server/site/" + "_api/web/lists/getbytitle('Announcements')"

The problem is that Microsoft removed the trailing / in the Current Site Url.  This broke many REST calls.

"http://server/site" + "_api/web/lists/getbytitle('Announcements')"

Is no longer valid.  The fix is to reopen all your workflows that are showing this problem, and add a preceding / before _api.  Like this:

"http://server/site" + "/_api/web/lists/getbytitle('Announcements')"

 

Now, with the February 2015 patch, Microsoft is fixing the workflow action again.  Guess what.

"http://server/site/" + "/_api/web/lists/getbytitle('Announcements')"

 

I think this looks dirty

 

Technically, your workflow probably won't break over this.

"http://server/site//_api/web/lists/getbytitle('Announcements')"

Should return you the same values as

"http://server/site/_api/web/lists/getbytitle('Announcements')"

 

But it looks terrible.

SPD2013 Workflow - how to check user is member of group

 

I want to describe a method that I use to check if a user is a member of a group.

 

Steps

  • Call a REST webservice
    • Reference MSDN for the correct API
    • Build a RequestURL and a basic RequestHeader
    • Figure out what the results mean
  • Wrap it up in a Workflow Custom Activity

 

API

MSDN (http://msdn.microsoft.com/en-us/library/office/dn268594(v=office.15).aspx - this needs to be a SharePoint Developer's home page) documents a few REST end points that I use for this.

http://msdn.microsoft.com/en-us/library/office/dn531432(v=office.15).aspx#bk_Group

Says you can get to a sharepoint group via:

  • http://<site url>/_api/web/sitegroups(<group id>)
  • http://<site url>/_api/web/sitegroups(<group name>)

The group also has a Users property that points to a Users Collection.

http://msdn.microsoft.com/en-us/library/office/dn531432(v=office.15).aspx#bk_UserCollection

This expands our example to:

  • http://<site url>/_api/web/sitegroups(<group id>)/users

For example:

 

The Users Collection does not have a method for testing if a user exists.  So I've taken the shortcut and basically brute force the service and just try to retrieve a user.  If you try to request a user that doesn't exist in the collection, it will just error, and I just catch that error.

 

SharePoint Designer workflow

 

image

 

Build Request Header

image

Both Accept and Content-Type needs to be "application/json;odata=verbose"

 

Build Request URL

image

Concatenate Current Site URL (which ends without a trailing /) and the earlier API.

Note my group name is 'john Members'

 

Call Web Service

image

 

Catch and process the result value.

image

 

The ResponseCode could be either OK or InternalServerError

Get a property from the returned Response variable "d/Title" would correspond to the Display Name of the user returned.  If the ResponseCode was Error, then there would be no value in the Response object.

 

 

Sandbox Custom Workflow Activity

 

In Visual Studio, these activities can be bundled into one single Activity that can be reused in SharePoint Designer.  I'll update this in a future blog post on Visual Studio.

 

 

Thoughts on checking nested group or AD group memberships

  • There are no way to check member with nested groups.  One possibility is to not think of it as membership, but think of it as whether the person has a certain permission.

    Does the current user have permission to do Contribute on the current Site. 
  • A more complicated thinking could be to create a list, kick out everyone except the group you are interested in, and check if the current user has permission to that list.

 

 

This Example in JavaScript

 

The more I work with SharePoint 2013 Workflows the closer parallels I see relating to a traditional programming language.  Here's the same function call in Javascript.

var promise = $.ajax({
        type: "GET",
        url: _spPageContextInfo.siteServerRelativeUrl + "/_api/web/sitegroups/getbyname('john Owners')/users/getbyid(" + _spPageContextInfo.userId + ")",
        headers: {
                "accept": "application/json;odata=verbose"
        },
        contentType: "application/json;odata=verbose",
        dataType: "json",
        cache: false,
        processData: true
});

promise.then(
        function (data, status) {

                if (status == "success" && data && data.d) {
                        var title = d.Title;

                }
                else {
                    // success, but no records - this can't really happen.
                }
        },
        function () {
                // not successful - usually not a member of that group
        }
);