“SafeQueryPropertiesTemplateUrl” error when calling the SharePoint _api

If you call the SharePoint 2013 REST API in your applications ensure that any requests originating from the client are sent from the current web base URL to avoid returning a SafeQueryPropertiesTemplateUrl error.

If the current site is
https://tenant.sharepoint.com/sites/mysitecollection/subsite1/subsite2 then it is very important that you submit API requests as
https://tenant.sharepoint.com/sites/mysitecollection/subsite1/subsite2/_api
and NOT as any of:

  • https://tenant.sharepoint.com/_api or
  • https://tenant.sharepoint.com/sites/mysitecollection/_api or even
  • https://tenant.sharepoint.com/sites/mysitecollection/subsite1/_api

The reason for this is that the current user must have access to the site addressed by the base URL of the API request (the bit before the _api). If the user cannot access this site then the request will fail. Unfortunately it doesn’t fail in the manner you might expect (i.e. a 401 access denied exception). A request that fails in this manner will return a 500 error. The specific exception details are as follows:

Exception class:
Microsoft.Office.Server.Search.REST.SearchServiceException

Exception message:
The SafeQueryPropertiesTemplateUrl "The SafeQueryPropertiesTemplateUrl "{0}" is not a valid URL." is not a valid URL.

SafeQueryPropertiesTemplateUrl Error received when attempting to hit _api endpoint from web to which the current user does not have access
Error received when attempting to hit _api endpoint from web to which the current user does not have access

Paul.

HTTP 405 Error in .NET Web App

I was recently told that an web app I had developed was returning an HTTP 405 error upon being freshly deployed. It took me way too long to realise that cause of the issue came down to missing files. Specifically, the complete folder structure had been deployed however the files at the top level web root were missing. These are files are rather critical.

They are the web.config and global.asax

If you are seeing this error, ensure these files have been deployed correctly and aren’t corrupt as a first point of call.

Receiving an HTTP 405 in IE11
Receiving a 405 in IE11

For SEO HTTP 405

  • Chrome: The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being used.
  • IE: HTTP 405 The website has a programming error. This error (HTTP 405 Method Not Allowed) means that Internet Explorer was able to connect to the website, but the site had a programming error.
  • Edge: HTTP 405 error That’s odd… Microsoft Edge can’t find this page

Paul.

PowerShell recursion with delegate functions, iterate all lists in all webs

There are many ways to iterate a collection in PowerShell. I just really like using delegate functions. This approach is not native PowerShell but utilises the .NET Action class as a function parameter. Using a delegate function approach, it is possible to create a recursive loop that can be very easily reused in the future just by providing an alternative Action.

The example code I provide below demonstrates how to create a delegate function in PowerShell, how to write a function that accepts one as a parameter, and provides some ready made samples for iterating SharePoint objects, specifically all webs or all lists. I am using some specific SharePoint objects in these samples, however the fundamental pattern can be used to effectively iterate any recursive structure.

foreachDecendentWeb : perform an action on every web below the provided web
foreachListInWeb : perform an action on every list in the provided web
foreachListInWebAndAllDecendentWebs : perform an action on every list in the current and all decendent webs

Delegates

Some notes

The below script references ‘TopOfScript.ps1’, it is specifically related to calling SharePoint CSOM from PowerShell. Read about it here on sharepointnutsandbolts.

Making the call, providing the delegate

The utility scipts, recursive functions accepting delegate parameters

Paul.

jsLink: how to display a custom ‘no items’ message

If you use jsLink to override the rendering of list views then you may have noticed that your custom jsLink no longer renders a message when there are no items returned in the view. I am going to discuss with code samples how to display a ‘no items’ message – or at least help you stop overriding it.

You have complete control over how list items are rendered using jsLink
You have complete control over how list items are rendered using jsLink

If, alternatively, you have a ‘no items’ message being displayed and just want to modify the text, try this link.

If you don’t know what jsLink is then it is worth learning about it. Try this link.

What am I doing wrong?

Chances you are making the same mistake that many people make. A mistake that has been replicated again and again online and doesn’t break anything but does prevent the display of the ‘no items’ message and the paging control. When you override Templates.Header you DO NOT need to override Templates.Footer in order to close tags which you opened in the header.

Although doing so seems to make sense, you can rest assured knowing that tags you open in the header will be closed auto-magically after the item templates have completed rendering. In fact, the footer template is rendered in a different table cell to the header and item templates when this all hits the page. Think of the footer template as a distinct block that is rendered after everything else rather than the end of the same block.

By overriding the footer template you are also inadvertently overriding the ‘no items’ message and the list view paging control. You can see exactly what you are overriding by inspected the default values for the templates. Below is snippet from clientrenderer.js which shows the default footer template.

So what should you do?

If you just want the default no items message and can get away with not overriding the footer template (as in the first code snippet), then great – you are all done.

If want a custom message then check out the link at the very top of the article (in summary: renderCtx.ListSchema.NoListItem = "Nada, nothing, zilch";).

If you want to override the footer template or perhaps you want the message to appear within a wrapper tag defined in the header or you want some custom logic behind which message to display then you can do that too – keep reading.

Doing it yourself

I’ve written a utility function that is based on the logic in the OOTB footer template that makes it easier to manage the ‘no items’ text. This function does NOT replicate the paging functionality. If you need paging and are overriding the footer template then you will need to replicate the paging functionality as well. You will need to look into clientrenderer.js to find out how MSFT do this.
Looking at this snippet you can see the if-else block where you can define custom messages for different list templates or if the lack of results has occurred only after a search term was provided. This sample should not be considered the superlative version, it just does a basic job in line with what happens by default.

Below are two examples of how you may want to use this. The first is by overriding the footer template, and the second is by overriding the header template. The advantage of sticking this code into the header template is that it allows you wrap the no items message in the same wrapper tags that you defined for the main content.

Paul.

For aiding findability:

  • There are no items to show in this view of the list
  • Your search returned no results
  • Some items might be hidden. Include these in your search
  • Still didn’t find it? Try searching the entire site.

DateTime validation message colour

There is a minor style bug in SharePoint 2013 (including SharePoint Online). The error message on a required DateTime field is not displayed in a manner consistent with other control validation errors. No it’s not just you, and no it’s not due to some conflicting CSS – it is a SharePoint bug.

Specifically I am referring to the page layout edit experience. A user fails to provide a value for a required DateTimeField control and the validation message is shown in the default text colour – ‘You must specify a value for this required field.’

redvalidation

For all other validation messages the SharePoint controls add the ms-formvalidation which sets a CSS rule to set the red colour. This is the only rule which the ms-formvalidation class sets and as such this is the only rule that should be applied to fix the issue.

I use the following CSS selector to resolve this issue:

Paul.

How to get your Office 365 Theme appearing immediately

With the addition of Office 365 themes we can provide some company branding throughout the Office 365 suite. But why does it take so long to propagate down throughout the SharePoint sites in the tenant?

Set an Office 365 theme
Set an Office 365 theme

These themes affect the suite bar across the top of the page. I see it often that after setting the Office 365 theme for a tenant, the theme gets applied to Mail, Calendar, Delve, etc but can seem to take a very long time before it appears on SharePoint sites.

Well it is actually just taking a long time to appear on SharePoint sites if you have visited SharePoint recently. The Office 365 theme colours are cached locally in the browser using local storage.

The local storage key that is used is: SPSuiteLinksJson. This appears to store everything that is required to render the entire suite bar.

Interestingly there is another local storage key which appears to store the date at which the suite bar data was cached: SPSuiteLinksDate. Presumably this is used to calculate when the cache should be refreshed from the server. I am not sure exactly how long this is yet. Please comment if you have figured out the cache timeout.

Regardless of its duration, if you want to force end users to get the latest theme more often you have the options of clearing either of these values or modifying the date field appropriately. But really, I can’t imagine why this would be so important although its nice to know that a bit of JavaScript can sort this out if need be.

Paul.

FYI – The value that is stored will be something like the following (taken from a test tenant):

{"SPSuiteVersion":2,"SPIsMobile":false,"CssUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/css/shellg2coremincss_40fd4be6.css","JsUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/JSC/CoreMinShellG2Bundle.js","NavBarData":{"AboutMeLink":{"BrandBarText":null,"Id":"ShellAboutMe","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"About me","Title":"Go to the My profile page","Url":"https://tenant-my.sharepoint.com/person.aspx"},"AdminLink":{"BrandBarText":null,"Id":"ShellAdmin","MenuName":"Admin centers","ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Admin","Title":"Go to the Office 365 admin center","Url":"https://portal.office.com/admin/default.aspx"},"AppsImage":null,"AppsLinks":null,"ClientData":"{\"AddBusinessUserUrl\":null,\"AdminHelpUrlFormat\":\"http:\\/\\/o15.officeredir.microsoft.com\\/r\\/rlidOfficeWebHelp?p1=O365ENTADMIN&clid=1033&ver=15&services=INTUNE_O365%2cYAMMER_ENTERPRISE%2cMCOSTANDARD%2cSHAREPOINTSTANDARD%2cEXCHANGE_S_STANDARD&p2=O365&HelpID={HelpID}\",\"AppHeaderLinkText\":null,\"AppHeaderLinkUrl\":null,\"AppLauncherErrorHelpUrl\":\"http:\\/\\/o15.officeredir.microsoft.com\\/r\\/rlidOfficeWebHelp?p1=O365ENTADMIN&clid=1033&ver=15&services=INTUNE_O365%2cYAMMER_ENTERPRISE%2cMCOSTANDARD%2cSHAREPOINTSTANDARD%2cEXCHANGE_S_STANDARD&p2=O365&HelpID=O365E_AppLTrustedSites\",\"AppSearchEnabled\":false,\"AppsCustomizationDisabled\":false,\"AppsDiscoverabilityDisabled\":false,\"AppsDragAndDropDisabled\":false,\"AppsGetAllAppTilesEnabled\":false,\"AppsPrePinnedDisabled\":false,\"AppsResizingDisabled\":true,\"AppsUpdateTimeSpan\":3600000,\"CDNUrl\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\",\"CardBundleJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/CardBundle.js\",\"ChatNotificationsDisabled\":false,\"ClearUserThemeCacheUrls\":\"[{\\\"WorkloadId\\\":\\\"AdminPortal\\\",\\\"WorkloadUrl\\\":\\\"https:\\/\\/portal.office.com\\/misc\\/expirecookies.aspx\\\",\\\"RequestType\\\":\\\"img\\\",\\\"NoRequestInSameWorkload\\\":true},{\\\"WorkloadId\\\":\\\"Sharepoint\\\",\\\"WorkloadUrl\\\":\\\"https:\\/\\/tenant-my.sharepoint.com\\/_layouts\\/15\\/ClearLocalCache.aspx\\\",\\\"RequestType\\\":\\\"iframe\\\",\\\"NoRequestInSameWorkload\\\":false},{\\\"WorkloadId\\\":\\\"Sharepoint\\\",\\\"WorkloadUrl\\\":\\\"https:\\/\\/tenant.sharepoint.com\\/_layouts\\/15\\/ClearLocalCache.aspx\\\",\\\"RequestType\\\":\\\"iframe\\\",\\\"NoRequestInSameWorkload\\\":false}]\",\"ClientTelemetryEnabled\":false,\"ClientTelemetrySource\":\"O365SuiteUX\",\"ClientTelemetryToken\":\"\",\"ClientTelemetryUIVersion\":\"1000\\/1.6.5.0\",\"ClientTelemetryUrl\":\"\",\"CollectorIds\":[\"SharePoint\",\"Graph\",\"FirstParty\",\"LineOfBusiness\"],\"ConsumerWorkloadUrls\":null,\"DefaultSavedUserUrl\":null,\"DisplayNameFormat\":null,\"EditPhotoPopupUrl\":\"https:\\/\\/outlook.office365.com\\/ecp\\/PersonalSettings\\/EditAccount.aspx?chgPhoto=1&exsvurl=1&realm={0}\",\"ExchangeWLADUpdateInterval\":28800000,\"FlexPaneDisabled\":false,\"GallatinLegalAlertEnabled\":false,\"GroupsDisabled\":true,\"HasEXOLicense\":true,\"HasSkypeLicense\":true,\"IdentitySwitcherEnabled\":false,\"ImmersiveProfileUrl\":\"profile\\/\",\"ImmersiveSettingsUrl\":\"settings\\/\",\"IsConsumerShell\":false,\"IsGuestMode\":false,\"IsIWDelveLinkPresent\":true,\"IsNFDDisabled\":false,\"IsNFDOnO365SuiteServiceEnabled\":false,\"IsO365SuiteServiceEnabled\":false,\"IsPartnerResellerPage\":false,\"IsRTL\":false,\"IsTenantDirSyncEnabled\":false,\"LoadUserThemesUrl\":\"https:\\/\\/portal.office.com\\/data.theme?action=tu&l=en-US&tt=G2&cdnver=16.00.0915.008\",\"LocalNotificationsDisabled\":false,\"LogArgLength\":1024,\"LogLevelSwitches\":[false,true,true,true],\"LogLevelSwitchesForPage\":null,\"LogUrl\":\"https:\\/\\/clientlog.portal.office.com\\/l.l\\/\",\"LyncIntegrationDisabled\":true,\"LyncIntegrationUrl\":\"https:\\/\\/swx.cdn.skype.com\\/shared\\/v\\/1.2.3\\/SkypeBootstrap.min.js\",\"MePhotoCachingDisabled\":false,\"MobileShellDisabled\":true,\"MultipleAADSwitchingEnabled\":false,\"MyAccountEnabled\":false,\"MyAccountUrl\":\"account\\/\",\"MyAppsUrl\":\"https:\\/\\/portal.office.com\\/myapps\",\"NeutralGrayColors\":[\"000000\",\"212121\",\"333333\",\"666666\",\"767676\",\"A6A6A6\",\"C8C8C8\",\"EAEAEA\",\"F4F4F4\",\"F8F8F8\",\"FFFFFF\"],\"NeutralStatusColors\":[\"A80F22\",\"D83B01\",\"2A8DD4\",\"107C10\"],\"NewAppLauncherHelpUrl\":null,\"NewAppNotificationEnabled\":false,\"NewMailNotificationsDisabled\":false,\"NoResultsHelpLinkUrl\":\"https:\\/\\/support.office.com\",\"NotificationsFlexPaneEnabled\":true,\"NotificationsSettingsV2Enabled\":false,\"PUID\":\"1003BFFD91368966\",\"PortalUrl\":\"https:\\/\\/portal.office.com\\/\",\"ProfileAboutCardApiUrl\":\"api\\/profile\\/data\",\"ProfileDoughboyUrl\":\"\\/images\\/profile\\/doughboy.png\",\"ProfileHeaderBGUrl\":\"\\/images\\/profile\\/profile-header-bg.jpg\",\"ProfileOrgChartCardApiUrl\":\"api\\/profile\\/org\",\"ProfilePaneEnabled\":false,\"RemindersNotificationDisabled\":false,\"RenderAsyncDisabled\":false,\"ResponsiveShellEnabled\":false,\"SHSID\":\"0822f3b5-7e00-4401-8436-34dc280519a9\",\"SavedUserUrl\":null,\"SettingsCardApiUrl\":\"api\\/settings\\/cards\",\"SettingsFlexPaneEnabledWorkloads\":\"ShellAdmin;AdminPortal\",\"SettingsPaneEnabled\":false,\"SettingsSearchClassifierUrl\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/JS\\/SettingSearch\\/SettingsSearchClassifier_en.js\",\"SettingsSearchNlrtUrl\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JS\\/SettingSearch\\/MicrosoftNaturalLanguageRuntime.js\",\"SettingsSessionStorageEnabled\":false,\"ShellCoreCSS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/css\\/shellg2corecss_9037d638.css\",\"ShellCoreCSSResourceKey\":\"shellg2corecss\",\"ShellCoreJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/CorePrimeShellG2Bundle.js\",\"ShellCoreJSResourceKey\":\"shellcoreprimeg2m\",\"ShellPlusCSS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/css\\/shellg2pluscss_1db8cf27.css\",\"ShellPlusCSSResourceKey\":\"shellg2pluscss\",\"ShellPlusJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/O365ShellG2Plus.js\",\"ShellPlusJSResourceKey\":\"shellplusg2m\",\"ShellPlusNarrowCSS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/css\\/shellg2plusncss_c867ccef.css\",\"ShellPlusNarrowCSSResourceKey\":\"shellg2plusncss\",\"ShellPlusNarrowJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/O365ShellG2PlusN.js\",\"ShellPlusNarrowJSResourceKey\":\"shellplusg2n\",\"ShellPlusTouchCSS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/css\\/shellg2plustcss_846fbfc5.css\",\"ShellPlusTouchCSSResourceKey\":\"shellg2plustcss\",\"ShellPlusTouchJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/O365ShellG2PlusT.js\",\"ShellPlusTouchJSResourceKey\":\"shellplusg2t\",\"ShellPlusWideCSS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/css\\/shellg2pluswcss_5c4da058.css\",\"ShellPlusWideCSSResourceKey\":\"shellg2pluswcss\",\"ShellPlusWideJS\":\"https:\\/\\/prod.msocdn.com\\/16.00.0915.008\\/en-US\\/JSC\\/O365ShellG2PlusW.js\",\"ShellPlusWideJSResourceKey\":\"shellplusg2w\",\"ShowMailNotificationCount\":false,\"ShowNewAppLauncher\":false,\"ShowNewMyAppsLink\":true,\"SkypeApiKey\":\"b32c0389-1143-4a42-b15f-7b860f9eafbb\",\"SocialNotificationsDisabled\":true,\"SuiteServiceCallsDisabled\":false,\"SuiteServiceUrl\":\"https:\\/\\/outlook.office365.com\\/owa\\/service.svc\",\"SwitchToBusinessUserUrl\":null,\"SystemNotificationsDisabled\":false,\"TID\":\"2fb81f0d-c6ef-4c83-b35c-9553261f9d9b\",\"TenantPrimaryColorShades\":[\"149B14\",\"18BD19\",\"18BD19\",\"1DE01D\",\"72D071\",\"C7E2C7\",\"FDFEFD\",\"FFFFFF\",\"FFFFFF\"],\"TenantThemeColors\":[\"1DE01D\",\"0CA597\",\"FFFFFF\",\"FFFFFF\"],\"TenantThemeCssUrl\":\"https:\\/\\/portal.office.com\\/data.theme?action=tc&tc=1DE01D|0CA597|FFFFFF|FFFFFF&tt=G2&tv=40357e1c-4ba4-4ff1-94ae-6df34f02fe07&l=en-US\",\"ThemeCssUrl\":\"https:\\/\\/portal.office.com\\/data.theme?action=tc&tc=1DE01D|0CA597|FFFFFF|FFFFFF&tt=G2&tv=40357e1c-4ba4-4ff1-94ae-6df34f02fe07&l=en-US\",\"ThemePanelEnabled\":true,\"UID\":\"f0f20750-f14a-416b-84da-03119aa4ac1e\",\"UPN\":\"ccdev5@tenant.onmicrosoft.com\",\"UserPersonalizationAllowed\":false,\"UserThemeId\":\"Base\",\"UserThemesPanelUrl\":null,\"UserThemesSettingsPageUrl\":\"https:\\/\\/portal.office.com\\/EditProfile15.aspx?serviceId=ThemeItem\",\"WorkloadId\":\"Sharepoint\"}","CommunityLink":{"BrandBarText":null,"Id":"ShellCommunity","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Community","Title":"Community","Url":"http://g.microsoftonline.com/0BX20en/142"},"CompanyDisplayName":"Content and Code","CorrelationID":"0822f3b5-7e00-4401-8436-34dc280519a9","CultureName":"en-US","CurrentMainLinkElementID":"ShellSharepoint","CurrentWorkloadHelpSubLinks":null,"CurrentWorkloadSettingsLink":null,"CurrentWorkloadSettingsSubLinks":null,"CurrentWorkloadUserSubLinks":null,"Dimensions":{"Top":50},"DownArrowImage":null,"DownWhiteArrowImage":null,"FeedbackLink":{"BrandBarText":null,"Id":"ShellFeedback","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Feedback","Title":null,"Url":"https://portal.office.com/SendSmile?wid=2"},"FlightName":"15GA,Exp180917IngestionFailedStatusTreat1,Exp130705C,Exp170859ihdc100,SE80186HRCSetupRedirect,SE130231CFRV2,SE130238GeminiSignupUI,SE160251CFRV2Detail,SE160252CFRV2Prefetch,SE160258ShellDelveLink,SE190296ShellVideoLink,SE190331GeminiPurchaseUI,SE190354IWSignupAlertsOn,SE190448AADUXChangePassword,SE190549NotificationsFlexPane,SE190532AggregatedServiceHealth,SE200626ShellSwayLink,SE200631PartnerFeedback,SE200681SwitchReadFromAPIService,SE220756PartnerClientTroubleshoot,SE220826AppStore,SE220829LOBApps,SE220825Office2016PreviewForPc,SE220822IntuneMDMStaged,SE240791PartnerUsageTracking,SE240809ShellServiceThemeCache,SE240810O365ImmersiveSettings,SE240827CortanaAccessSettings,SE240829ShellClassNoteBookLink,SE240843PartnerSettingsPageFeature,SE240844HelpFlexPane,SE240866MobileAdmin,SE240880OAuthAuthenticationForAPIService,SE240881GeminiCatalogUI,SE240883ShellNewMyAppsLink,SE250903CFRV3,SE250928ShellPowerBILink,SE260930UnifiedSetup,SE240865ShellStaffNoteBookLink,SE271003CFRV3CsvExportEnabled,SE271005CutoverProxy,SE271017SwitchReadFromAPIService,SE271018CFRV3TS,SE271014AdvSetupImap,SE281032AetherSubscriptionsUI,SE291071PartnerResellerMultiPartner,SE281034OfficeHome3,Exp9015C","FlipHelpIcon":false,"FooterCopyrightLogoTitle":null,"FooterCopyrightText":null,"FooterICPLink":null,"FooterLogoImage":null,"HasTenantBranding":true,"HelpImage":null,"HelpLink":{"BrandBarText":null,"Id":"HelpLink","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Help","Title":null,"Url":"&services=INTUNE_O365%2cYAMMER_ENTERPRISE%2cMCOSTANDARD%2cSHAREPOINTSTANDARD%2cEXCHANGE_S_STANDARD&p2=O365"},"ImageClusterUrl":null,"IsAuthenticated":true,"LegalLink":{"BrandBarText":null,"Id":"ShellLegal","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Legal","Title":"Legal","Url":"http://g.microsoftonline.com/0BX20en/721"},"LogoIconID":"o365logo","LogoImage":null,"LogoNavigationUrl":"https://portal.office.com/Home","LogoThemeableImage":null,"MenuTitleText":null,"MyProfileUrl":"https://portal.office.com/EditProfile15.aspx?ServiceID=LanguageItem","NavBarAriaLabel":null,"NotificationsBellIconImage":null,"NotificationsCountLabelText":null,"NotificationsHighIconImage":null,"NotificationsLowIconImage":null,"NotificationsMediumIconImage":null,"NotificationsPopupHeaderText":null,"NotificationsProgressIconImage":null,"O365SettingsLink":{"BrandBarText":null,"Id":"ShellO365Settings","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Office 365 settings","Title":null,"Url":"https://portal.office.com/settings/"},"PartnerLink":null,"PoweredByText":"powered by {0}","PrivacyLink":{"BrandBarText":null,"Id":"ShellPrivacy","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Privacy","Title":"Privacy","Url":"http://g.microsoftonline.com/0BX20en/138"},"SPO_MySiteHostUrl":null,"SPO_RootSiteUrl":null,"SessionID":"e88c267e-5b6e-49ab-8f2d-766510e25c46","SettingsImage":null,"SignOutLink":{"BrandBarText":null,"Id":"ShellSignout","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Sign out","Title":"Sign out of Office 365 and return to the Sign-in page","Url":"https://login.microsoftonline.com/logout.srf?ct=1443035468&rver=6.4.6456.0&lc=1033&id=271346"},"TenantBackgroundImageUrl":null,"TenantLogoNavigationUrl":null,"TenantLogoUrl":{"BrandBarText":null,"Id":null,"MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":null,"Title":"Content and Code","Url":"data:image/png;base64,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"},"ThemeColors":null,"TransparentImageUrl":null,"TruncatedUserDisplayName":null,"UpArrowImage":null,"UseSPOBehaviors":false,"UserDisplayName":"Content and Code Dev 5","WorkloadLinks":[{"BrandBarText":"Outlook","Id":"ShellMail","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Mail","Title":"Go to your email","Url":"https://outlook.office365.com/owa/?realm=tenant.onmicrosoft.com&exsvurl=1&ll-cc=1033&modurl=0"},{"BrandBarText":"Outlook","Id":"ShellCalendar","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Calendar","Title":"Go to your calendar","Url":"https://outlook.office365.com/owa/?realm=tenant.onmicrosoft.com&exsvurl=1&ll-cc=1033&modurl=1"},{"BrandBarText":"Outlook","Id":"ShellPeople","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"People","Title":"Go to People and contacts","Url":"https://outlook.office365.com/owa/?realm=tenant.onmicrosoft.com&exsvurl=1&ll-cc=1033&modurl=2"},{"BrandBarText":null,"Id":"ShellNewsfeed","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Newsfeed","Title":"Go to your Newsfeed","Url":"https://tenant-my.sharepoint.com/default.aspx"},{"BrandBarText":null,"Id":"ShellDocuments","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"OneDrive","Title":"Go to OneDrive for Business","Url":"https://tenant-my.sharepoint.com/personal/ccdev5_tenant_onmicrosoft_com/_layouts/15/start.aspx#/_layouts/15/onedrive.aspx"},{"BrandBarText":null,"Id":"ShellSites","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Sites","Title":"Go to team sites","Url":"https://tenant-my.sharepoint.com/personal/ccdev5_tenant_onmicrosoft_com/_layouts/15/start.aspx#/Social/Sites.aspx"},{"BrandBarText":"Outlook","Id":"ShellTasks","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Tasks","Title":"Go to Outlook Web App for Tasks","Url":"https://outlook.office365.com/owa/?realm=tenant.onmicrosoft.com&exsvurl=1&ll-cc=1033&modurl=3"},{"BrandBarText":null,"Id":"ShellOfficeGraph","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Delve","Title":"Go to Delve","Url":"https://tenant-my.sharepoint.com/_layouts/15/me.aspx?origin=shell"},{"BrandBarText":null,"Id":"ShellVideo","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":null,"Text":"Video","Title":"Go to Video for Office 365 to share videos","Url":"https://tenant.sharepoint.com/portals/hub/_layouts/15/videohome.aspx"},{"BrandBarText":null,"Id":"ShellWordOnline","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Word Online","Title":"Go to Word Online","Url":"https://office.live.com/start/Word.aspx?auth=2"},{"BrandBarText":null,"Id":"ShellExcelOnline","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Excel Online","Title":"Go to Excel Online","Url":"https://office.live.com/start/Excel.aspx?auth=2"},{"BrandBarText":null,"Id":"ShellPowerPointOnline","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"PowerPoint Online","Title":"Go to PowerPoint Online","Url":"https://office.live.com/start/PowerPoint.aspx?auth=2"},{"BrandBarText":null,"Id":"ShellOneNoteOnline","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"OneNote Online","Title":"Go to OneNote Online","Url":"https://www.onenote.com/notebooks?auth=2"},{"BrandBarText":null,"Id":"ShellSway","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Sway","Title":"Go to Sway","Url":"https://www.sway.com?auth_pvr=OrgId&auth_upn=ccdev5@tenant.onmicrosoft.com"},{"BrandBarText":null,"Id":"ShellOfficeStore","MenuName":null,"ServiceId":null,"SubLinks":null,"TargetWindow":"_blank","Text":"Office 365 Store","Title":null,"Url":"https://portal.office.com/store"}]},"SharedCSSTouchWideUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/css/shellg2corewcss_8b4fb7b2.css","SharedJSTouchWideUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/JSC/CoreShellG2BundleW.js","SharedCSSTouchNarrowUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/css/shellg2corencss_18d28bae.css","SharedJSTouchNarrowUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/JSC/CoreShellG2BundleN.js","SharedCSSTouchDeviceUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/css/shellg2coretcss_ba6efae3.css","SharedJSTouchDeviceUrl":"https://prod.msocdn.com/16.00.0915.008/en-US/JSC/CoreShellG2BundleT.js","TenantPrimaryColorShades":["149B14","18BD19","18BD19","1DE01D","72D071","C7E2C7","FDFEFD","FFFFFF","FFFFFF"],"UserThemePrimaryColorShades":["104A7D","0D62AA","106EBE","0078D7","2B88D8","71AFE5","C7E0F4","DEECF9","EFF6FC"],"UserPersonalizationAllowed":false,"ThemeVersion":"G2","ShellRequestId":"aadc309d-b069-3000-9493-98edca352038"}

Paul.

Dynamically generating complex pre-refined search result page URLs

I while ago I blogged about creating a static link to a pre-refined (pre-filtered) search page. This post follows that idea to it’s natural conclusion by providing a number of JavaScript functions which can dynamically create search result page URLs. These URLs will look something like this:

https://tenant.sharepoint.com/search#Default=%7B%22k%22%3A%22article%22%2C%22r%22%3A%5B%7B%22n%22%3A%22RefinableString20%22%2C%22t%22%3A%5B%22%5C%22%C7%82%C7%824275696c64%5C%22%22%5D%2C%22o%22%3A%22OR%22%2C%22k%22%3Afalse%2C%22m%22%3A%7B%22%5C%22%C7%82%C7%824275696c64%5C%22%22%3A%22Build%22%7D%7D%2C%7B%22n%22%3A%22RefinableString21%22%2C%22t%22%3A%5B%22%5C%22%C7%82%C7%824c6f6e646f6e%5C%22%22%5D%2C%22o%22%3A%22OR%22%2C%22k%22%3Afalse%2C%22m%22%3A%7B%22%5C%22%C7%82%C7%824c6f6e646f6e%5C%22%22%3A%22London%22%7D%7D%5D%7D

The provided scripts support filtering on:

  • a search term
  • multiple refiners
  • multiple values for a refiner, or
  • any combination of the above

It would be worth reading the intro of my earlier article to get a better understanding of what is happening in the snippets provided in this post.

Default Enterprise Search Centre
Default Enterprise Search Centre

OF NOTE:

  • As the most common usage will surely be to produce search result page URLs that are refined on a single value, I have written an ‘overload’ function that simplifies calling the method in this scenario
  • The ‘search page URL’ can be provided to the functions in a number of ways including:
    • “/search” : to the web. The default page for that web. In the case of an Enterprise Search Centre this will be the ‘Everything’ search results page
    • “/search/Pages/peopleresults.aspx” : to the page
    • Use an absolute URL if you are out of the context of the SharePoint Online tenant in which the search page resides. This will be true for provider hosted add-ins (apps)
    • If you are writing your own refiner, then pass an empty string and set window.location.hash to the result of the function
  • This script has no dependencies on other libraries (jQuery, SP.js, etc)
  • The hex encoded string must be UTF-8 encoded. JavaScript is natively UTF-16. The particular scenario where this raised an issue for me was the wide-ampersand character which is often used instead of a standard ampersand as it is XML friendly. ‘unescape’ returns a UTF-8 encoded string and is used to force the required encoding. Thanks to ecmanaut for this solution
  • I took inspiration for the stringToHex method from a post by pussard

The functions:

var getPreRefinedSearchPageUrl = function (searchPageUrl, searchTerm, managedPropertyName, managedPropertyValue) {
  return getComplexPreRefinedSearchPageUrl({
    searchPageUrl: searchPageUrl,
    searchTerm: searchTerm,
    refiners: [
      {
        managedPropertyName: managedPropertyName,
        managedPropertyValues: [
          managedPropertyValue
        ]
      }
    ]
  });
};

// input:
// {
//   searchPageUrl: "/search/Pages/results.aspx",
//   searchTerm: "",
//   refiners: [
//     {
//       managedPropertyName: "RefinableString08",
//       managedPropertyValues: [
//         "Human Resources"
//       ]
//     }
//   ]
// }
var getComplexPreRefinedSearchPageUrl = function (data) {
  var searchObj = {
    "k": data.searchTerm,
    "r": []
  };
  for (var i = 0; i < data.refiners.length; i++) {
    var refiner = data.refiners[i];
    var searchObjRefiner = {
      "n": refiner.managedPropertyName,
      "t": [],
      "o": "OR",
      "k": false,
      "m": {}
    };
    for (var j = 0; j < refiner.managedPropertyValues.length; j++) {
      var refinerValue = refiner.managedPropertyValues[j];
      // Force UTF8 encoding to handle special characters, specifically full-width ampersand
      var managedPropertyValueUTF8 = unescape(encodeURIComponent(refinerValue)); 
      var managedPropertyValueHex = stringToHex(managedPropertyValueUTF8);
      var managedPropertyValueHexToken = "\"ǂǂ" + managedPropertyValueHex + "\"";
      searchObjRefiner.t.push(managedPropertyValueHexToken);
      searchObjRefiner.m[managedPropertyValueHexToken] = refinerValue;
      searchObj.r.push(searchObjRefiner);
    }
  }
  var seachObjString = JSON.stringify(searchObj);
  var searchObjEncodedString = encodeURIComponent(seachObjString);
  var url = data.searchPageUrl + "#Default=" + searchObjEncodedString;
  return url;
};

var stringToHex = function (tmp) {
  var d2h = function (d) {
    return d.toString(16);
  };
  var str = '',
    i = 0,
    tmp_len = tmp.length,
    c;
  for (; i < tmp_len; i += 1) {
    c = tmp.charCodeAt(i);
    str += d2h(c);
  }
  return str;
};

These are examples of how to call the function that are defined above.

var complexUrl = getComplexPreRefinedSearchPageUrl({
  searchPageUrl: "/search/Pages/results.aspx",
  searchTerm: "article",
  refiners: [
    {
      managedPropertyName: "RefinableString20",
      managedPropertyValues: [
        "Build", "Land"
      ]
    },
    {
      managedPropertyName: "RefinableString21",
      managedPropertyValues: [
        "London"
      ]
    }
  ]
});
var basicUrl = getPreRefinedSearchPageUrl("/search/Pages/results.aspx", "", "RefinableString20", "Build");

Paul.

Calling the Office 365 Unified API from JavaScript using ADAL.js

The goal of this post is to provide very basic ‘hello world’ example of how to call the Office 365 Unified API (aka Graph API) using JavaScript and the ADAL.js library. This has recently become possible (May 2015) now that CORS is supported by the Office 365 APIs (most of the individual endpoints support it as well as the unified API).

The ADAL library simplifies the process of obtaining and caching the authentication tokens required to retrieve data from Office 365. It is possible to avoid the ADAL library and handle this yourself, although I would recommend doing so as a learning exercise only.

I failed to find a simple example of how to achieve this, my search results often filled with examples of calling the APIs from server-side code or else utilising the Angular.js framework. This example is based on a more complex example.

The following snippet will log to the browser console the results of a call the to files endpoint of the Office 365 unified API, which will return a JSON object containing information about the files in the current users’ OD4B.

Before it will work you must complete the following steps (as described in detail here):

  1. Register an Azure Active Directory App. Note that *every* Office 365 subscriptions comes with AAD and supports the creation of an app
  2. Associate the required ‘permissions to other services’, in this case ‘Read users files’ via the Office 365 Unified API
  3. Allow implicit flow

Not covered explicitly in the above article but also critical are the following steps:

  • Get the App’s Client ID and copy it into the snippet
The App Client ID
The App Client ID
  • Get the Azure Active Directory subscription ID and copy it into the snippet
The subscription ID in Azure Active Directory
The subscription ID in Azure Active Directory

Once the above steps have been completed, you can try out the snippet by embedding in a Script Editor web part, or you can run it externally to SharePoint as part of, say, a provider hosted app.

NOTE: I found that the call to files endpoint is failing for certain users. I am still unsure whether this is due to external vs internal users (is working for internal [.onmicrosoft.com] users) or whether it could be licencing issue. The /beta/me endpoint is working in all cases.

Paul.

GLOSSARY

CORS: Cross-Origin Resource Sharing
ADAL: Active Directly Authentication Library
OD4B: OneDrive 4 Business

Activate Feature on Solution Activation

This is really just a “note to self” because I always forget where this settings lies.

Activate On Default is property of a feature. You can find it by opening the Feature Designer and then looking in the properties window. It is the first property under the SharePoint section.

This property determines whether the feature will be activated when the solution is activated. My preferred approach when developing a publishing portal that requires web templates, is to only have the ‘web templates’ feature activated this way and have any/all other features activated along with the root site web template.

ActivateOnDefault

Note that this property has no impact on the set of features which are activated when deploying the solution via Visual Studio. You can configure this somewhat using the ‘Active Deployment Configuration’ property of the project. You can try something like the following from Mavention for more granularity: Mavention – activating selected features

Paul

Search Schema Scoping in SharePoint Online

For solutions that are contained in a single site collection, or span a small number of site collections, or are in a tenant where the other solutions are not trusted or are unknown, then I have a strong preference to use site collection scoped search schema rather than tenant scoped.

Side note: I am yet to come across a situation where I would use site scoped search schema. In my mind, the existence of search schema at this level only serves to confuse.

Search Schema hierarchy is SharePoint Online.
Search Schema hierarchy is SharePoint Online. There is also site scoped search schema at lowest level which is not present here.

For those that aren’t fully aware, search schema (the set of managed properties that are accessible via the search framework) can be provisioned at the tenant, site collection, or site scope. These scopes are hierarchical such that managed properties are inherited from the tenant scope down to the site scope but can be overridden along the way. There are some good articles that delve into this in more detail.

By provisioning search schema at the site collection level you are mitigating the risks of errors related to other solutions changing the properties which your solution relies upon. This is especially relevant in SharePoint Online where all solutions in the tenant have to share a common set of RefinableTypeXX managed properties.

There are some important exceptions, of course.

  1. People Search, a.k.a User Profile Search, a.k.a Local People Results
    In SharePoint Online, people properties are indexed on a very slow schedule. We requested more information from Microsoft regarding this and were told that this schedule is ‘confidential’. I have found that when using site-collection scoped managed properties it can take *weeks* for them to get populated. I have found much better (although still poor) performance using tenant scoped properties (usually within a few days). Assuming you do require custom search schema for people properties I would still recommend provisioning all remaining managed properties (all those not mapped to people properties) at the site collection level.
  2. Many site collections
    Of course, having many site collections which require the same search schema is valid reason to go tenant scoped. This is purely due to management of the properties going forwards. A solid scripted deployment procedure should not care if you are provision search schema to 1 or 50 site collections – but anyone maintaining the solution will definitely care if they have update 50 schemas manually, or are suddenly required to script something which they feel should be *easy*. Even in this scenario you should still consider how much you trust other solutions in the tenant against the impact of finding out that one day your managed properties are mapped incorrectly. Depending on your solution this could lead to errors that are left undetected, or conversely obviously break your home page.

Paul.