Microsoft Flow, Office 365, SharePoint

Configure settings for access requests and sharing using Microsoft Flow and REST

I’m currently building a solution for provisioning Microsoft Teams for a customer, using Microsoft Flow. I don’t have the privilege to use Azure, PowerShell, CSOM or JavaScript in their environment – so I have to stick with the built in connectors and use various REST endpoints in SharePoint Online and Microsoft Graph.

In Microsoft Teams (and in Office 365 groups), you need to be an Owner to add a new Member. However, members of the associated SharePoint team site can add people to the site.

site-permissions-share-site-only

We wanted to align the membership management between SharePoint and Teams, and set out on a path to solve this task. I started looking into the access request settings of the site, and all boxes are enabled by default for a Team site associated with Microsoft Teams.

access-requests-settings-default

There are several examples on how to use PowerShell or JavaScript to modify these settings, but I found no examples on how to use SharePoint’s REST APIs. Luckily I found a comment somewhere on Stack Exchange mentioning that you can use MERGE to modify a property on various SharePoint endpoints. So armed with this knowledge I found out that I could use the /_api/Web endpoint to configure the MembersCanShare property and RequestAccessEmail property.

  • To uncheck Allow members to share the site and individual files and folders you simply set MembersCanShare to false.
  • To uncheck Allow access requests you simply set RequestAccessEmail to “” (empty string)

Now we miss one important ingredient for using this endpoint to change the web object, and that is the MERGE method.

Add the following HTTP headers to your request:

{
  "accept": "application/json;odata=verbose",
  "content-type": "application/json;odata=verbose",
  "X-HTTP-Method": "MERGE"
}

And POST the following JSON payload:

{
  "__metadata":{"type":"SP.Web"},
  "MembersCanShare": false,
  "RequestAccessEmail":""
}

Here’s how you can use the “Send an HTTP request to SharePoint” action in Microsoft Flow.

flow-rest-disable-access-requests-and-member-sharing

If you want to uncheck “Allow members to invite others to the site members group…“, you can use the /_api/Web/AssociatedMemberGroup endpoint to configure the AllowMembersEditMembership property the same way.

Add the same HTTP headers as mentioned above and POST the following JSON payload to the endpoint:

{
  "__metadata": {
    "type": "SP.Group"
  },
  "AllowMembersEditMembership": false
}

prevent-members-from-managing-membership

That’s it! Your access request settings should now look like this!

access-requests-settings-modified

 

 

Advertisements
Standard
Microsoft Flow, Office 365, Yammer

Governing Yammer groups using Microsoft Flow

I’m currently helping a customer to rollout Yammer to ~4,000 employees across the world. The goal is to improve knowledge sharing and communication across locations, projects and departments. Microsoft Teams is also on the roadmap, and will be rolled out later this year.

where-to-start-the-communication

We have tailored Microsoft’s “Where to start the conversation” message and our strategy is to use Yammer for internal communication only and use public groups to better address the vision about breaking down the silos. We even defined a KPI for number of private vs public Yammer groups.

Unfortunately, it is currently not possible in Yammer to proactively enforce the policies to:

  • prevent who can create groups
  • prevent creation of external groups
  • prevent creation of private or public groups

We decided instead to reactively govern Yammer groups using Microsoft Flow and Yammer REST APIs. Now, there is a built-in Yammer connector in Flow, but the current functionality doesn’t support our requirements. We decided to build two flows, one for governing external groups and one for private groups. Each flow runs on a daily schedule.

Flows

The flow that governs private groups, emails the private group owners with a message that they need to open or delete their group. The screenshot below shows the effect on private groups after one week of running the flow.

private group log

The flow that governs external groups, emails the external group creators and delete their group.

Account for running the flows and accessing the Yammer APIs

  • The account can be a cloud only account and doesn’t need any admin roles in Office 365.
  • The account must be licensed for Yammer to use the APIs, for Outlook to send emails and SharePoint for logging purposes.
  • The account must be a Verified admin in Yammer to delete or get details about groups the account isn’t member of.

Register a Yammer application and create an access token

  1. Goto https://www.yammer.com/client_applications and sign-in with the verified admin account.
  2. Click “Register New App”, fill in the required fields and click “Continue”. You only need to think about the Redirect URI, as you’re mainly interested in the generated client id and secret in addition.

    Register New App

  3. Next go to https://www.yammer.com/oauth2/authorize?client_id=YOUR_CLIENT_ID_HERE&response_type=code&redirect_uri=https://localhost
  4. Click “Allow” to grant the app to access Yammer on the account’s behalf.

    Allow the app to act on your behalf

  5. Now, extract the code appended to your redirect URI from the browser’s address bar.

    https://localhost/?code=CODE_TO_EXTRACT_HERE

  6. Next go to https://www.yammer.com/oauth2/access_token.json?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&code=CODE_YOU_EXTRACTED_FROM_REDIRECT_URI
  7. Extract the access_token from the JSON in the response from above and send it in the Authorization header when invoking the REST endpoints.

{
“access_token”:{
…,
“token”:”THIS_IS_THE_ACCESS_TOKEN”,
…,
“expires_at”:null
},
“user”:{…},
“network”:{…}
}

Yammer API endpoints used in the flows

These are the REST endpoints we used in our flows.

You can access documentation about the REST API on Yammer Developer Center
https://developer.yammer.com/docs

Implementing the flows

The flows both starts with a scheduled trigger and a set of actions for variable initialization

flow - init variables

Content mode is then set to private for access to groups the account is not member of.

set content mode to private

The Yammer groups endpoint doesn’t support the HTTP connectors built-in pagination, so a Do Until loop is used to handle pagination of the groups that are returned 50 at-a-time.

groups pagination

Then we union the groups and paginate before moving on to parsing and filtering the groups.

groups union

This is how we filter for private groups

filter private groups

And this is how we filter for external groups. Be aware that external groups are also private (just in case you want to process both private and external groups in same Flow)!

filter external groups

We process the private groups in a Apply-to-each loop, get the members of the group and filter for group admins.

process private groups

How we filter for group admins

filter for group admins

Next we send an email to all private group administrators in a Apply-to-each loop.

email to private group admins

The final steps of the flow sets the private content mode back to default and logs the number of private and total groups to a SharePoint list. The numbers are needed for the KPI measures.

set content mode and log to sharepoint

For each external group, we get the creator, send the person an email and delete the group.

get group creator and delete group

Measuring success

We believe that using Microsoft Flow will help us reach our business goals. We have defined a strategy and success plan for knowledge sharing, and defined KPIs for measuring progress and realization of these goals.

Yammer measurement plan

I have shown you how to manage Yammer groups using Microsoft Flow and Yammer REST APIs. Don’t stop here! Start exploring other opportunities and use your favorite REST API to manage what’s important for you and your business.

Happy flowing!

Standard
SharePoint

SharePoint Hybrid App Launcher Explained

There aren’t too much information available about the hybrid app launcher currently, so the purpose of this blog post is to fill in some missing pieces.

We recently enabled hybrid app launcher in a SharePoint 2013 farm, and we weren’t really sure how it would look or work.

How did it look like? Did it reflect the Office 365 version?

No! It didn’t exactly look like the current version in our Office 365 tenant, and it didn’t even have all the same apps (or tiles) in it.

This is how the Office 365 app launcher looks like in my client’s tenant. Yes, I’m a First Release user, and these are the apps I’ve pinned to Home.

Now, this is how the Hybrid App Launcher looks like in a SharePoint 2013 site with default masterpage.

Unfortunately, it looks nothing like the one in Office 365. The whole user experience is different, including colors, layout and content. The general impression is that the design is outdated.

The biggest disappointment however, is that I don’t get all the apps that I’m licensed to and can see in Office 365.

Off course we’re using custom master pages in our current SharePoint 2013 farm, and our custom styling doesn’t make it better.

In order to configure the hybrid extensible app launcher in SharePoint Server 2013, you need to have configured hybrid sites and the July 2016 PU for SharePoint Server 2013 (or newer) must have been installed.

The hybrid app launcher is installed using PowerShell

Install-SPFeature SuiteNav

Then the (hidden) feature must be enabled per site collection using

Enable-SPFeature suitenav -url <SiteCollectionURL>

It would have been great if the feature was web application scoped to show the app launcher for new sites. Now we need to schedule a custom script to run instead, or maybe implement feature stapling.

When we first enabled the suitenav feature for our site, we couldn’t see the app launcher. We soon found out that our custom masterpage was missing the following delegate control

<SharePoint:DelegateControl id="ID_SuiteBarDelegate" ControlId="SuiteBarDelegate" runat="server" />

The delegate control performs the following server side code:

  1. Registers CSS (_layouts/15/SuiteNav.css)
  2. Registers a script link (_layouts/15/SuiteNav.js)
  3. Registers a startup script (RenderSuiteNav) which is passed the parameter SuiteNavRestMethod with value Microsoft.SharePoint.Portal.SuiteNavData.GetSuiteNavData.

The hybrid app launcher renders asynchronously by the SuiteNav.js and the data is fed from the _api/Microsoft.SharePoint.Portal.SuiteNavData.GetSuiteNavData rest method.

I didn’t see any invocations to this rest method using my browser developer tools, so I assume it is invoked server side. The api is available both in SharePoint Online and in SharePoint Server, but it only returns content when invoked on the server. Here’s what it returned for my account:

{
    "DoNotCache": false,
    "NavBarData": {
        "AboutMeLink": {
            "Id": "ShellAboutMe",
            "TargetWindow": null,
            "Text": "About me",
            "Title": "Go to the My profile page",
            "Url": "https:\/\/company-my.sharepoint.com\/person.aspx"
        },
        "ClientData": "{\"HasEXOLicense\":true,\"IsRTL\":false,\"MyAppsUrl\":\"https:\\\/\\\/portal.office.com\\\/myapps\"}",
        "CommunityLink": {
            "Id": "ShellCommunity",
            "TargetWindow": "_blank",
            "Text": "Community",
            "Title": "Community",
            "Url": "https:\/\/answers.microsoft.com\/en-US\/msoffice"
        },
        "CorrelationID": "8ea9cb7a-8567-4505-b120-8be421a01fc0",
        "CurrentMainLinkElementID": "ShellSites",
        "CurrentWorkloadHelpSubLinks": null,
        "CurrentWorkloadSettingsLink": null,
        "CurrentWorkloadSettingsSubLinks": null,
        "CurrentWorkloadUserSubLinks": null,
        "FeedbackLink": {
            "Id": "ShellFeedback",
            "TargetWindow": "_blank",
            "Text": "Feedback",
            "Title": null,
            "Url": "https:\/\/portal.office.com\/SendSmile?wid=2"
        },
        "FlipHelpIcon": false,
        "HasTenantBranding": true,
        "HelpLink": {
            "Id": "HelpLink",
            "TargetWindow": "_blank",
            "Text": "Help",
            "Title": null,
            "Url": "&amp;services=RMS_S_PREMIUM%2cINTUNE_A%2cRMS_S_ENTERPRISE%2cAAD_PREMIUM%2cMFA_PREMIUM%2cDeskless%2cFLOW_O365_P2%2cPOWERAPPS_O365_P2%2cTEAMS1%2cPROJECTWORKMANAGEMENT%2cSWAY%2cINTUNE_O365%2cYAMMER_ENTERPRISE%2cOFFICESUBSCRIPTION%2cMCOSTANDARD%2cSHAREPOINTWAC%2cSHAREPOINTENTERPRISE%2cEXCHANGE_S_ENTERPRISE%2cEXCHANGE_S_FOUNDATION%2cBI_AZURE_P0%2cRMS_S_ADHOC&amp;p2=O365"
        },
        "IsAuthenticated": true,
        "LegalLink": {
            "Id": "ShellLegal",
            "TargetWindow": "_blank",
            "Text": "Legal",
            "Title": "Legal",
            "Url": "https:\/\/www.microsoft.com\/online\/legal\/v2\/?docid=13&amp;langid=en-US"
        },
        "LogoIconID": "o365logo",
        "LogoNavigationUrl": "https:\/\/www.office.com\/1?auth=2&amp;home=1&amp;from=ShellLogo",
        "O365SettingsLink": {
            "Id": "ShellO365Settings",
            "TargetWindow": null,
            "Text": "Office 365 settings",
            "Title": null,
            "Url": "https:\/\/portal.office.com\/settings\/"
        },
        "PinnedApps": [
            {
                "AriaLabel": null,
                "BackgroundColor": "#0072c6",
                "BrandBarText": null,
                "CollectorId": null,
                "FontIconCss": "wf-o365-newsfeed",
                "IconFullUrl": null,
                "Id": "Newsfeed",
                "IsNewGroup": false,
                "LaunchFullUrl": "https:\/\/sharepoint-my.company.com:443\/default.aspx",
                "Size": 2,
                "TargetWindow": null,
                "Title": "Newsfeed"
            },
            {
                "AriaLabel": null,
                "BackgroundColor": "#0072c6",
                "BrandBarText": null,
                "CollectorId": null,
                "FontIconCss": "wf-o365-cloud",
                "IconFullUrl": null,
                "Id": "Documents",
                "IsNewGroup": false,
                "LaunchFullUrl": "https:\/\/company-my.sharepoint.com\/_layouts\/15\/MySite.aspx?MySiteRedirect=AllDocuments&amp;Source=SP2015",
                "Size": 2,
                "TargetWindow": null,
                "Title": "OneDrive"
            },
            {
                "AriaLabel": null,
                "BackgroundColor": "#0072c6",
                "BrandBarText": null,
                "CollectorId": null,
                "FontIconCss": "wf-o365-sharepointlogo",
                "IconFullUrl": null,
                "Id": "Sites",
                "IsNewGroup": false,
                "LaunchFullUrl": "https:\/\/company-my.sharepoint.com\/_layouts\/15\/MySite.aspx?MySiteRedirect=AllSites&amp;Source=SP2015",
                "Size": 2,
                "TargetWindow": null,
                "Title": "Sites"
            },
            {
                "AriaLabel": "Go to Delve",
                "BackgroundColor": "#0072C6",
                "BrandBarText": null,
                "CollectorId": "FirstParty",
                "FontIconCss": "wf-o365-pulselogo",
                "IconFullUrl": null,
                "Id": "OfficeGraph",
                "IsNewGroup": false,
                "LaunchFullUrl": "https:\/\/company-my.sharepoint.com\/_layouts\/15\/me.aspx?Origin=HybridShell16",
                "Size": 2,
                "TargetWindow": null,
                "Title": "Delve"
            },
            {
                "AriaLabel": "Go to Video for Office 365 to share videos",
                "BackgroundColor": "#0072C6",
                "BrandBarText": null,
                "CollectorId": "FirstParty",
                "FontIconCss": "wf-o365-videologo",
                "IconFullUrl": null,
                "Id": "Video",
                "IsNewGroup": false,
                "LaunchFullUrl": "https:\/\/company.sharepoint.com\/portals\/hub\/_layouts\/15\/videohome.aspx?from=3",
                "Size": 2,
                "TargetWindow": null,
                "Title": "Video"
            }
        ],
        "PrivacyLink": {
            "Id": "ShellPrivacy",
            "TargetWindow": "_blank",
            "Text": "Privacy &amp; cookies",
            "Title": "Privacy &amp; cookies",
            "Url": "https:\/\/www.microsoft.com\/online\/legal\/v2\/?docid=18&amp;langid=en-US"
        },
        "SessionID": "cd6527d8-e055-4d4c-8ffe-90db7df99b8e",
        "SignOutLink": {
            "Id": "ShellSignout",
            "TargetWindow": null,
            "Text": "Sign out",
            "Title": "Sign out and return to the Sign-in page",
            "Url": "https:\/\/login.microsoftonline.com\/logout.srf?ct=1489754717&amp;rver=64.4.6456.0&amp;lc=1033&amp;id=501392"
        },
        "TenantBackgroundImageUrl": null,
        "TenantLogoNavigationUrl": null,
        "TenantLogoUrl": null,
        "TruncatedUserDisplayName": null,
        "UserDisplayName": "Petter Skodvin-Hvammen"
    },
    "SPSuiteVersion": 2
}
Standard
Office 365, Search, SharePoint

Add Search to your Office 365 app launcher

At Puzzlepart we’re all about Office 365, and despite all the negative talk lately, Yammer is our go-to place for knowledge sharing and staying updated.

But Yammer got good company by Office 365 Sites, Wikis, Blogs, Videos, OneDrive, Delve and last but not least……… search.

Now, I would argue that having a search box in the Office 365 suite bar across all of the workloads would have been the ideal solution for quick information discovery and retrieval.

Unfortunately Microsoft doesn’t support that, but luckily there is an alternative way. Yes, you can customize the Office 365 app launcher and add a shortcut to your SharePoint Online search center.

Here’s how you do it:

  1. Open your Company Profile in your Office 365 Admin center
  2. Select Custom tiles and add a custom tile using the plus (+) icon
  3. Specify Tile name, URL, Description and Image Url.
    search-tile-app-launcher
  4. Next, select your tile in your My Apps and click Pin to app launcher
    search-tile-app-launcher-pin
  5. Voilá. Your shortcut to search is ready.

    search-tile-app-launcher2.PNG

Microsoft has made detailed instructions on how to customize the app launcher here

https://support.office.com/en-us/article/Add-custom-tiles-to-the-My-apps-page-and-app-launcher-1136115a-75af-4497-b693-640c4ce70bc6#

Standard
Search, SharePoint

Teach your users how to search with hints

search-hints

I’m currently involved in several search improvement projects for multi-national companies running SharePoint 2013. We started interviewing stakeholders, setup user surveys, analyzed WebTrends statistics and search query logs, and our backlogs quickly filled up.

One topic we discussed repeatedly was the need to help our users becoming more familiar with the search solution. Here are some examples:

  • Usage statistics and survey results shows that only a few of the search verticals (displayed below the search box) were used
  • Users says they want advanced search, but very low usage was the reason we removed it
  • Search refiner usage is low
  • Average number of query terms used is close to 1

Instead of giving the users the out-of-the-box advanced search, we decided to give then hints about how to use the advanced features instead. We also decided to use search hints instead of writing a customized component for contextual news results based on the current user.

Here’s how we did it:

  • Created custom site columns, content type and a list
  • Created custom JavaScript to read the hints from the list, display a random hint, suppress hidden hints, show next, etc…
  • Created a custom search results control template to display the hints if there were no spelling suggestions (did you mean)

The end result is shown in the image above.

 

Standard
Search, SharePoint

How to connect search result web parts using query groups

If you add multiple search result web parts to your search result page, they will be disconnected from the search box. Your web parts will reflect query in the url of the page, but when you change the query in the search box, only the default results will be updated, not the additional search results web parts.

Now, you can fix this in the UI by editing the Search Box web part and select all relevant web parts under Send queries to other Web Parts on this page.

If you export your search box web part, you will now get an xml file with GUID references to the search result web parts.

A better approach is to use logical names instead of GUIDs.

For each of your search result webpart files, specify the QueryGroupName property

Default results webpart

...
<property name="QueryGroupName" type="string">Default</property>
...

Promoted results webpart

...
<property name="QueryGroupName" type="string">PromotedResults</property>
...

People results webpart

...
<property name="QueryGroupName" type="string">PeopleResults</property>
...

News results webpart

...
<property name="QueryGroupName" type="string">NewsResults</property>
...

In your search box webpart file, reference the query group names in the QueryGroupNamesJson property.

Search box webpart

...
<property name="QueryGroupNamesJson" type="string">["Default","PromotedResults","PeopleResults","NewsResults"]</property>
...
Standard
Search, SharePoint

Hybrid Search Bonanza – European SharePoint Conference 2015

How can you fulfill the enterprise search vision in your organization?

In this speak I demonstrated how to search across two separate domains, using ASFS, multiple authentication providers in the default zone, a custom claims provider against a SQL database, FIM sync from AD to SQL and a custom security trimmer.

I also demonstrated how you can use the new Cloud Search Service Application in SharePoint 2013 (With September 2015 Update) to provide a unified search experience across on-prem and Office 365. I showed how you can crawl SharePoint on-prem content, a file share, web sites and a SQL database using BCS and let your users search for this in their Office 365 tenant. Further I demonstrated how to surface on-prem content using Content Search Web Parts and how you can find cloud content in your on-prem search center, like SharePoint Online content, Delve profiles, Delve blogs and Office videos.

If you want to see how I enabled the content source refiner, use link to search configuration below.

Slides and resources related to my presentation in Stockholm on November 10th 2015.

Standard