"Mr Sharepoint" is a blog based on RSS for everything related to sharepoint, it collects its posts from many sites in order to facilitate the updating to the latest technology
Thursday, April 18, 2019
Tuesday, April 16, 2019
Wednesday, April 10, 2019
Tuesday, April 9, 2019
Friday, March 15, 2019
Monday, January 28, 2019
Saturday, December 29, 2018
Tuesday, December 18, 2018
SharePoint Online Search Isn’t Displaying What I Expect – Part 2 – EnableDynamicGroups
This occasional series probably ought to be titled “How to solve a search problem by asking Mikael Svenson” (@MikaelSvenson), but he probably doesn’t want to be the world’s help desk for search. Luckily, I know him.
In my previous post, I showed how to simplify your code by using PnPJS and async/await. I didn’t focus much on what I was trying to accomplish. The goal of the search call was to get a list of all the sites (STS_Site) which the current user could access. We’ve built a My Team Sites SPFx Web Part and this call is in the services for it.
Over several weeks, we’ve been trying to get the Web Part to display what we want – which wasn’t as simple as we would have thought. If you work with the search APIs all the time, these things may be old hat to you, but since there is usually a big gap in between the times I use them, my memory can get a little fuzzy.
In the prior post in this series, I talked about how the way that the search API trims duplicates – which may make total sense in many ad hoc search results – can cause you to miss results you actually need. By setting TrimDuplicates to false, we can ask the search API to return all the results.
We ran into another search “feature” in this instance. Since we’re trying to retrieve all the sites to which the current user has access, we’re asking the search API to return objects where contentclass=STS_Site. I’d already turn off duplicate trimming – can’t catch me twice on that! But we still weren’t getting all of the sites back. Even more strangely, we were getting some modern sites back, but not others. The sites we were getting back and those we weren’t were darn near identical in their set up and use. Because they represented a project covering a specific scientific topic, the names of the sites were almost identical.
Here’s an abstraction of what we were seeing:
Site 1 | Site 2 | |
Site Type | Modern Team Site | Modern Team Site |
URL | /sites/SITE-CHR-204 | /sites/SITE-CHR-207 |
Title | SITE-CHR-204 | SITE-CHR-207 |
Permissions* | Owner | Owner |
contentclass | STS_Site | STS_Site |
* In this case, my permissions as I was testing, but we checked with a few other people’s views as well.
As you can see, the only difference we could spot was the single character difference in the Title and URL for the site. Duplicate trimming could certainly have kicked in there, but we had already turned that off. (If you have any ideas of what else might be different, I’m all ears!)
After a discussion with Mikael, he suggested adding EnableDynamicGroups to the call. Magical – it worked! I can’t for the life of me understand why it worked, since we were seeing probably a good half of the modern Team Sites already, but since it worked, I guess we should just be happy with that. As Mikael told me, “turn off duplicate trimming and add that, and things usually work.”
If you’re using PnPJS and async/await- as I recommended in my prior post, then the change is in lines 14-20. We’ve simply added the EnableDynamicGroups property and set it to true.
private async _getSiteData(): Promise<ISPSite[]> { var thisDomain: string = location.host.split(".")[0]; var exclusions: string[] = ["https://" + thisDomain + "-my.sharepoint.com", "https://" + thisDomain + ".sharepoint.com/portals/personal"]; var exclusionString: string = " -Path:" + exclusions.join(" -Path:"); exclusionString += " -Path=https://" + thisDomain + ".sharepoint.com"; try { let result = await sp.search(<SearchQuery>{ Querytext: "contentclass:sts_site " + exclusionString, RowLimit: 500, TrimDuplicates: false, Properties: [{ Name:"EnableDynamicGroups", Value: { BoolVal: true, QueryPropertyValueTypeIndex: QueryPropertyValueType.BooleanType } }], SelectProperties: ["Title", "Path", "SiteLogo"] }); return this.processSearchResults(result); } catch (e) { console.error(e); return null; } }
by Marc D Anderson via Marc D Anderson's Blog
Wednesday, December 12, 2018
Using PnPJS and Async/Await to Really Simplify Your API Calls
Anyone who knows us at Sympraxis realizes that Julie (@jfj1997) is the master developer. I consider myself quite capable, but Julie is always at least three steps ahead of me. She often leads me by the nose to a better pattern or practice, and it always seems to be a good idea.
Over the last few days – at Julie’s suggestion – I finally decided to try using the PnPJS library with a couple of SPFx Web Parts I’m working on. Even though I was on all the calls in the beginning of the effort to build it, and on some level it’s a “modern” SPServices, I decided a while back that I didn’t really like the way it looked. I understand REST just fine, I figured. Why would I need a helper library?
I’ve been cruising along just fine, building great stuff for our clients, but Julie started telling me recently that PnPJS and async/await was reducing her code size significantly. It sounded too good to be true, frankly. So I figured I should give it a try.
Here are a couple of examples to demonstrate how right Julie was and how much code I’ve been writing that I didn’t need to write. (Allow me to get away without doing everything I need to in the try/catch here. That’s not the point I’m trying to make. Watch for a post on Julie’s blog about how to use PnPJS’s Logging package with try/catch.)
Calling Search to Get Sites
In this instance, switching to PnPJS and async/await didn’t greatly diminish my code size, but I do think it makes it more readable. It also runs significantly faster.
Here’s the starting code. It’s a simple call to the search endpoint requesting Sites (as in Site Collections in the old parlance) with some specific exclusions. The Web Part this is in shows all the sites the current user has permissions to see. It’s like the modern Sites Web Part, but that Web Part doesn’t allow us to:
- Show *all* the sites the user can access, and
- Control the styling of the output
The end result is a listing of the sites, showing their icon and Title as a link to the site.
This is one of the data calls as it started:
private _getSiteData(): Promise<any> { var thisDomain: string = location.host.split(".")[0]; var exclusions: string[] = ["https://" + thisDomain + "-my.sharepoint.com", "https://" + thisDomain + ".sharepoint.com/portals/personal"]; var exclusionString: string = " -Path:" + exclusions.join(" -Path:"); exclusionString += " -Path=https://bluebirdbio.sharepoint.com"; var url: string = this.context.pageContext.web.absoluteUrl + "/_api/search/query" + "?querytext='contentclass:sts_site " + exclusionString + "'" + "&selectproperties='Title,Path,SiteLogo'" + "&rowlimit=500"; return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1,{ headers: { "Accept": "application/json;odata.metadata=none" } }) .then((response: SPHttpClientResponse) => { return response.json(); }); }
And this is what it looks like using PnPJs and async/await.
private async _getSiteData(): Promise<ISPSite[]> { var thisDomain: string = location.host.split(".")[0]; var exclusions: string[] = ["https://" + thisDomain + "-my.sharepoint.com", "https://" + thisDomain + ".sharepoint.com/portals/personal"]; var exclusionString: string = " -Path:" + exclusions.join(" -Path:"); exclusionString += " -Path=https://bluebirdbio.sharepoint.com"; try { let result = await sp.search(<SearchQuery>{ Querytext: "contentclass:sts_site " + exclusionString, RowLimit: 500, SelectProperties: ["Title", "Path", "SiteLogo"] }); return this.processSearchResults(result); } catch (e) { console.error(e); return null; } }
Even with the added try/catch the code is smaller, and since we’re building up the request with the library, we get help from our IDE (I use WebStorm) because the library is strictly typed. In the first example, since I’m building the request in the URL string, it’s easier to type an error into my code.
Getting List Data
In this example, I’m retrieving values from what I think of as a reference list for a more full-fledged application-in-a-Web-Part.
public getShipmentStatuses(serviceProps: IServiceProperties): Promise<IStatus[]> { return new Promise((resolve, reject) => { var urlList: string = `${serviceProps.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('SL_ShippingStatuses')/RenderListDataAsStream`; var requestOptions = this.spHttpOptions.getShipmentStatuses; serviceProps.context.spHttpClient.post(urlList, SPHttpClient.configurations.v1, requestOptions ).then((responseList: SPHttpClientResponse): Promise<{ value: any }> => { return responseList.json(); }) .then((responseJson: any) => { var shipmentStatuses: IStatus[] = []; for (var i = 0; i < responseJson.Row.length; i++) { var thisRow = responseJson.Row[i]; var thisShipmentStatus: IStatus = new Status(); thisShipmentStatus.Id = thisRow.ID; thisShipmentStatus.Title = thisRow.Title; thisShipmentStatus.SortOrder = thisRow.SortOrder; thisShipmentStatus.Follows = thisRow.Follows; thisShipmentStatus.CanBeCancelled = thisRow.CanBeCancelled === "Yes"; shipmentStatuses.push(thisShipmentStatus); } resolve(shipmentStatuses); }) .catch((err) => { reject(err); }); }); }
This turns into:
public async getShipmentStatuses(serviceProps: IServiceProperties): Promise<IStatus[]> { try { let items = await sp .web .lists .getByTitle("SL_ShippingStatuses") .items .select("Id", "Title", "SortOrder", "CanBeCancelled") .orderBy("SortOrder") .get(spODataEntityArray<Item, IStatus>(Item)); return items; } catch (e) { console.error(e); return null; } }
Much less dense and easier to follow, I think.
The other thing I’m taking advantage of in PnPJs here is Entity Merging. You can see how simple that is in line 12 above. Rather than building up my object in what amounts to a constructor, the Entity Merging package takes care of it for me. All I have to do is tell it what interface to use. (Above, it’s IStatus.)
Summary
Sometimes it’s important for old dogs to learn new tricks. In software development, it’s really a requirement. It didn’t take me anywhere as near as much time to take these steps forward, and I’m already seeing performance improvements and simpler code – both wins for my clients. If Julie recommends something – we should ALL listen!
by Marc D Anderson via Marc D Anderson's Blog
Thursday, October 4, 2018
Friday, September 14, 2018
Tuesday, August 21, 2018
Monday, August 20, 2018
Thursday, July 26, 2018
Group By Content Type in Modern Lists and Libraries
IMO, this should be an easier thing to do. It’s a VERY common request from my clients, and for years we’ve had to hack this together. There are currently two UserVoice items about it that I can find – here and here – but not nearly enough votes to get on the radar of the product team. You can help me fix that, right? Vote early, and vote often!
Ideally, in the modern view, we should be able to simply click on Group by Content Type in the UI. No joy there. We get that option for virtually every other column type – including the dread Managed Metadata column!
No big deal, right? We’ll just go into the view settings and add the grouping. Nope. Never was going to happen. Content Type is missing from the list of columns in the dropdown.
Luckily, in the second of those UserVoice items, a wonderful person named Eric Ivarsson gives us the trick we need to make this work. I worry about tricks like this because they could always stop working, but this one seems to have worked for a while now (at least a year), so I’m willing to use it and recommend it.
If you simply add this query string to the end of your view:
?groupBy=ContentType
you will get a view which groups by Content Type. You don’t even have to be displaying the Content Type column for this to work. If you have other groupings in place YMMV, but it accomplishes the main requirement here.
Make sure the “B” in groupBy is capitalized, or it won’t work.
You can then save the view from the UI, and even use the same view name. The setting will become embedded in the view (somehow) and you’ll be all set.
Again, if you use this trick, please vote up the two UserVoice items so that we can stop using it.
by Marc D Anderson via Marc D Anderson's Blog
Monday, July 23, 2018
Whither a New Intranet on Modern Office 365?
I was thinking about this a while back, and I started a conversation with a few of my go to smart people, Sue Hanley (@susanhanley) and Julie Turner (@jfj1997). If you were starting today with someone on a new Intranet on Office 365 – someone who has never used SharePoint at all – where would you put the Intranet “home page”?
The question seems simple, but the root site of a tenant is a “classic” site, and most people would want their new Intranet to be “modern” all the way. So a shallow page in the root which just contains links to some other Hub Sites (maybe Departments, Projects, etc.) or an Intranet at /sites/Intranet? This isn’t just an academic discussion – I did a half day workshop with a new client a while ago, and it was hard to answer this without equivocating.
Every organization I’ve ever worked with wants that one landing page that meets virtually every need. We end up parsing that down into realistic implementations, but there has to be a single launchpad. The Intranet home page is almost always that, and it has to be somewhere sensical. It’s hard enough convincing people that http://tenant.sharepoint.com makes sense. Office.com or portal.office.com, etc. are non-starters in those discussions in my experience.
Most user bases are made up of people who rarely remember the URL to the Intranet or Office 365 unless they use it every day, and in many organizations, that’s just not the case. (The ones that end up using SharePoint for document storage only – a large proportion – fall into this category.) Anything we can do to make that one landing page both compelling to visit (always a content generation challenge, rarely a technical one) and easy to find contributes to success.
Sue’s take on it was this set of three options, which seem to cover the current realm of possibilities.
- Option 1 (Microsoft’s “official” answer, perhaps): Make a modern page on that classic site and make it the home page. My take is that this “page” would feel weird and be marginally useful. It would also have the Quick Launch, which wouldn’t feel right. Since it’s classic, the root site can’t be a Hub Site, so the only way to get useful content onto it would be via development or roll up Web Parts. I am a strong believe in very little manual management of content on an Intranet home page; most should bubble up from other sites.
- Option 2: Dave Feldman’s (@bostonmusicdave) recommendation: Create a Communication Site that will be the new “home” (odds are it would live at /sites/Intranet or /sites/WhateverYouCallYourIntranet) and add a redirect from the classic home. I think that the user experience will be weird for this, but you could test it. Option 3 is a variation on this.
- Option 3: Make a modern site (/sites/Intranet or whatever the name is) and ask users to go to that site. If you don’t advertise contoso.sharepoint.com as the home of the intranet, users won’t go there. If you give them the URL (and make a shortcut for it), that will become the home – because it’s where people expect to find it. You can also make that site a Featured site on SharePoint home. Then, you can design your “home home” to have whatever user experiences you want – directory to the hubs, of course, plus enterprise news that will show up on the mobile app, plus all the other great things that home pages should have to engage the users.
None of these feel “right” to me. I know we’re in a transition phase, but we’re always in one these days. In my experience, most people want the home page of their Intranet to be a no-brainer URL. The most logical place for that on Office 365 is the root of the tenant. Sue raised the question of whether it really matters: using DNS you could come up with a short link that points to /sites/Internet, but I feel strongly that to most people this would just feel wrong – the URL in the browser would look too “messy” to them. (IT people probably wouldn’t care, Corporate Communications or the like would probably freak out.)
The other angle here is how we can future proof that landing page. If we decide to use https://ift.tt/2JPd5XT and we can eventually move it to https://ift.tt/2Lyn2xC, then we have what amounts to a non-durable link.
What do you think about all this? I’d like an Option 4 – make the product do what we really want. This would mean that the root site of a tenant could be a Communication Site or a modern Team Site, and we’re off to the races. I’m sure we’ll get there soon enough.
What are you doing out there, whether you’re a consultant or work within an organization?
by Marc D Anderson via Marc D Anderson's Blog
Thursday, July 12, 2018
Debugging Issues with Content Type Hub Syndication
As we’re moving into the “modern” era in SharePoint Online, we will need to use the Content Type Hub more and more frequently. This is due to the fact that we are moving from a subsite-laden pyramid of sites to a flat topology of sites. Where we used to build our information architecture – Site Columns and Content Types – in the root site of the pyramid and utilize it in all the subsites, we now need to elevate our information architecture to the Content Type Hub if we want to ensure reuse and clean management.
The publishing process from the Content Type Hub is sort of crappy, to be honest. The capabilities there were really cool when they first came out in SharePoint 2010, but it truly needs some love from the SharePoint folks in Redmond. I know it’s on their list, but in the meantime, we need to struggle with it as is.
To help with this, we can turn to some tooling in in each site that I’m not sure a lot of people know about. In every Site Collection – therefore every modern site – there’s a Content type publishing settings page that can help at
/_layouts/15/contenttypesyndicationhubs.aspx. This page allows you to “pull” on the next publishing round, and also lets you drill into any issues occurring in the process.
If we look at the Content Type Publishing Log, we can see if there are any conflicts blocking the publishing process.
There are a number of reasons why you may not see your Content Type available in a particular Site Collection. Here are two examples.
- The error in the green box tells us that there is a conflict with the Content Type name CDM Document already being in use.
- The error in the red box tells us the Site Column Indication is already in use in the current Site Collection.
Either of these situations can occur when you prototype something in a particular site and then build the Content Type in the Content Type Hub for wider usage. As we move to modern sites, I expect this will happen to us more and more often.
In either case, this points out the importance of having a holistic view of your information architecture. The flat nature of modern sites is making all of this far more difficult, as we must use the Content Type Hub to get any common structures in place.
Note: Sharegate is EXCELLENT for copying Site Columns and Content Types from one site to another. It doesn’t eliminate any of these problems, but it makes moving your information architecture around much easier.
So the next time you think the syndication (publishing) process just isn’t working because you don’t see the Content Types where you expect them, don’t blame the timer jobs – be sure to look at the Content Type Publishing Log. You’re likely to be able to track down the issue.
by Marc D Anderson via Marc D Anderson's Blog
Monday, June 18, 2018
Batch Conversion of HTML Files to PDFs Using Adobe Acrobat DX
Lots of times as a consultant, I need to get content in one format into another format. It usually means a lot of content, stored in all sorts of ways. I cut my teeth on this kind of work way back in the 1980s when I worked at Bain & Company. Consulting teams there would bring us all sorts of junk (“some sort of magnetic media”) and want to make sense of it. Because of this, I’m rarely scared of data conversions. It’s also one of the reasons I love Sharegate so much – for SharePoint, it makes much of this type of work easy!
In one of my current projects, I need to export content from multiple SharePoint 2007 sites to be loaded into a third party platform. The content is stored in lists and I can easily export it with Sharegate. However, there is a Rich Text field – the most important field, of course – and the content in that field can far exceed Excel’s 32k character limit in a single cell.
Instead, I’m using Microsoft Access (more about this later) and some VBA to export the Rich Text to HTML files. I’ll have thousands of them, and I need to convert them to PDFs.
Surprisingly, Binging and Googling took me to all sorts of fly-by-night possibilities, but not the most obvious one. Adobe Acrobat DC does this right out of the box! I didn’t even find this on the Adobe support site.
And you’re done. In my first test, converting about 400 HTML files, some with embedded images, took about 10 minutes.
This post is as much for future me as it is for anyone else, but I hope it will help some others out.
by Marc D Anderson via Marc D Anderson's Blog
Wednesday, March 28, 2018
Office 365 Hub Sites Are Here! A Few Tips
It’s great to see the Hub Sites promised at Ignite last fall rolling out finally. Hub Sites will be the capability which allows us to associate related “modern” sites with each other. This is probably most common in Intranets, but other examples might be a set of Project sites, a multi-layered HR sites, etc. Keep in mind that the maximum number of Hub Sites today is 50, so plan carefully. (That’s a bigger number than you think, in most cases.)
I’ve been able to play with them a little in my tenant, and I thought I’d post a few observations based on confusion I felt as well as questions I’ve seen. You can read all the nitty-gritty details in this post from Mark Kashman (@mkashman) Organize your intranet with SharePoint hub sites, but here’s what I felt I needed to know.
To setup a new Hub Site in your Office 365 tenant, you only need to run a little Powershell. (I wish the way we have to first work with new capabilities was never spelled P-o-w-e-r-s-h-e-l-l, but that’s what it is. Build us a UI!) I don’t run Powershell all that often, so I needed a little refresher. Here are the steps:
- Open the SharePoint Online Management Shell. If you haven’t installed it yet, you can download it here.
- Run the following command:
Connect-SPOService -Url https://sympraxis-admin.sharepoint.com -Credential $userCredential Register-SPOHubSite https://YourTenant.sharepoint.com/sites/YourHubSite
Yes, you must be a SharePoint Admin (or above) to do this. If you aren’t one, go bake your SharePoint Admin some chocolate chip cookies. - If you are fine with any Site Owner adding their site to this Hub Site, you can stop here. However, you’ll probably want to lock it down to a specific set of people. To do this, you create a mail-enabled group and give it permission to add sites to the Hub Site. For my Hub Site at /sites/Intranet, this goes like:
Connect-SPOService -Url https://sympraxis-admin.sharepoint.com -Credential $userCredential Grant-SPOHubSiteRights -Identity https://sympraxis.sharepoint.com/sites/Intranet -Principals sympraxisemployees@sympraxis.onmicrosoft.com -Rights Join
I ran the Powershell and I have a new Hub Site. I’ve associated one other “modern” site with it. A few questions came to me immediately as I poked around. Melissa Torres at Microsoft was kind enough to answer my questions on the Tech Community,
After I set a new theme on the Hub Site, is there a lag before I see it on the associated site(s)?
Yes it’s not automatic, can take up to 2 hours.
I have two different browser tabs open to the Hub Site. I manually added a link to the associated site in one tab, but don’t see it in the other, even after a Ctrl-F5. My guess is something is cached that shouldn’t be.
Correct it is due to caching. We’re working on addressing this issue of switching between tabs.
I published a News item in the associated site, but don’t see it in the Hub Site. What’s the delay supposed to be there? Are all News items rolled up?
We use search to rollup content from the associated sites so it’s subject to that, and yes all news items rollup.
In case you were wondering, the HubNav ends up right below the Top placeholder, and above the mainContent.
Don’t have access to a Hub Site? Having questions about how it all works? Feel free to add your questions in the comments, and I’ll try to reply with answers.
by Marc D Anderson via Marc D Anderson's Blog
Thursday, March 22, 2018
Monday, February 5, 2018
PowerApps: Setting a SharePoint List Lookup Column
PowerApps is really cool, and I really mean it. Unfortunately, it’s been moving forward so fast that it’s really hard to find clear information about how to do things. Examples you may find from just a few months ago may not look at all like what you see on your screen. (This post may be obsolete by the time I finish writing it!) Because of this, seemingly simple things can become a time consuming headache.
Here’s how I solved one of my recent headaches. There are lots of piece parts about this out there, but I haven’t found a soup to nuts description of how to do it.
I have a simple demo list called Beach Inspections – which are nice to think about in February in New England! It’s a simple list, and for this discussion, we’re only going to care about one column: Country. Country is a lookup column into the Countries list’s Title, which contains all the world’s countries. When we create a new beach inspection, we’ll usually want to preset the Country based on what we already know, whether it be the user or the beach name or whatever; it doesn’t really matter what. The challenge is in setting the value of the lookup field in PowerApps.
Let’s look at this from the initial setup of the PowerApp for the list. When you first click on Customize forms, you’ll see a bunch of screens and will arrive on one which looks something like this:
PowerApps generates a pretty decent form for you right off the bat – in theory, it may be what you need, but I expect in most cases we will edit this to make it our own.
To set things up to preset the Country lookup column, click on SharePointForm1 in the left panel (1) to display the forms properties in the right panel. You’ll see the data connection on in the right panel (2) at the top.
With the Data panel open, you can see the available columns from the data connection (your list – my Beach Inspections list). Many of them will already be checked, meaning they are included in the default form.
Next to the Country column, there’s a little icon showing what type of control is rendered on the form. When you click it, you’ll see the options below.
By default – even though the column is a Lookup column – the Edit selection control is set. This is the part I missed in all of the posts I found. Laura Rogers post (in the References section below) was the closest, but this part was omitted. You need to switch to the Edit lookup control to make things work.
Now, to set the value in the field, you can add a JSON object to the filed. In my case, I’m keeping it simple. I’m setting the default value to the United States with the JSON below:
{ '@odata.type' : "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference", Id: 236, Value: "United States" }
This works because the item for United States in the Countries list has an ID of 236. If you’re familiar with the way lookup columns are stored in lists, this should make a lot of sense to you. In my Beach Inspections list, the Country value will be stored as:
236;#United States
I know, I’m being very USA-centric. I could just have easily used:
{ '@odata.type' : "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference", Id: 6, Value: "Andorra" }
as the default, because the item in the Countries list for Andorra has an ID of 6.
Of course, the values for Id and Value could be provided by values in other fields, a calculation, or many other things. Again, I’m trying to keep things simple here.
One problem we see right away is that the field looks weird in PowerApps when we add this JSON:
That’s because PowerApps by default displays the value for
'@odata.type'. I’m going to go out on a limb here and guess that no one will ever want to see that value in their form. The way to fix it is to change the setting in Value1 below the Default setting for the field.
You can decide to display the Value or the Id you’ve provided in the JSON. Generally speaking, I’m guessing you’ll want to show the Value, so select it from the dropdown.
Now, when I create a new item in my Beach Inspections list using the PowerApps form, I see this:
The default for Country is set to United States, but I can decide to change it if I’m inspecting all the beautiful beaches in Andorra. (Hint: Andorra is a land-locked country, so there’s very little work for us beach inspectors.)
References
PowerApps: Set SharePoint Lookup Field by Laura Rogers (@wonderlaura)
by Marc D Anderson via Marc D Anderson's Blog