Category Archives: Technical Tip

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,iVBORw0KGgoAAAANSUhEUgAAALYAAAAyCAYAAADx0SHKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAA3nSURBVHhe7Z0LrB1VFYb7oIUWWmoRyqMWaFUIKVAUoRSJIC8rUEhVkApWtNIKVAGRQnlDBBXkEQQUVBQEBUEwSiQ2oEEQwSCovBJIbNGAAQMSeT9av3/Omt2ZM3tm9px77mHu6fzJnz2z9r/2njOz7sye/Zg7rEGDBg0aNGjQoEGDHmLlypXjVq1atTXpHvAT8AvwqATnwwPRfJh0Ghxjrg0a1AsE53gL1EXwDLbPDCX600k/Bz/YBHmDWoBAnERAfsqC0xu4VUg5S+FsON6qaNCgdyAIxxJ8c2Clu3MoKfdU+BG217IqGzQYXBBw7yfgToLfYPvb8HL4Y3hZHJjdImUeRbqxVV1fcKAz4V8GmVdYdQ26AM7n1XZeH4Er4KvwDfgWXEngRWDzdyTeAB0IKVd3723tcOoJDnBvDnZQQR3LrLoGXQDn8247tYVANyiBLVL2GXCmHVL9wME1gT3EwPl8xwNbpPz6BjcHVimw0b/ZAW+36hp0AZzPP9rlKAS6QQ1skTpOh1vZodUHHFRwYKP9J9yoKnGdYNU1GCA4nxpAWdG6IsVAN+iBLVLPyXCiHWI9wAFVCewV5tbgHQCXYB2uwfHwqdYVKQa6ngS2SF1HkA63Q80FmhEJluo7BgfUBPYQAed/Hy6Dgqh2gW2cYYfqwDFMhDuSdxCpRkH1h3mi8SvwCLgv+Rry794oJ4U1gT0EwLkfD0/jMtQ2sKnvONKRcATb28GFMHjACK26ETWXZVP72Z2DQqoE9nJza9BjcO5ncwniAKjrHVvHdgBc7MsznkT+0XCB8RjZkhps6m05hO132c+vDgqozR2b8vXYmg73h3pMpf5y2d8BLoaavzCfQ5piWZWAn9qqu0E9Gs+G52PTCV1Eujtcz6RdAeVOgntCzao7AZ5mqR7DGqYufLkmfzS6k3WMItsDCmzs58Fr4a/hnXAZvAVeAc/y+QyElHks1MxCzWMZaT/LQTa4Mfk6R8cn/HSdtzdZNeDY08CmDAWvRs0eMj4Gn4avWzUO2KIfRbo5XGZmB2waafsmmyOiwkuAdluoUbuXWiX4Qf5r8OdwFrsdveDguylUAD/cKjUfaN4m+T2p7lKZuRjYt8GeDJSOApt9Be7D8C3l+0Def0huhM6vU1LWV+EObAddH0FafHaEX0uUoxitdh3MKQhouxHY6gJ804osBLrt4XvhM2bygvxjrHgvyF8XXgZzL6gP6N+G32dzrBVVCvRj4Lnw1VYp1YDfvXBzKy4C5oNgMmAqBTapnkp3Qf0BlQKdoBuJq7Mq8f806Tr2EyoDf53HeYnyZltWGHDoaRubMqoE9q7wEdvNBZonSbx/0eRtAh9oKbMg73F4CNTd5RUzp4D9HpLS5gm6yfDBllca2O+DaidPgGpaqD/68/DPkSABbMvhZlasytVLWTJoggMbKqi9x1QEfPRH/R02Xb2hxG9vO/QBgbKGU9b+iXJ3saxy6CBwCgLaXt+xf2ObhUCniT/vtiocZCPr8UjkAXmvwC1NrmNT+84L8m4lyX0ckq/fpT+wDLD/G44zaQpkr0XepS3lamC7g2Q4HAvbA6dKYAcNv/uAr0Y4U3WXER8FYmGzgXw1N3RHHqNtM3uhstBpdY/KVtNuE8sqBsLaBraAVrPW9MKofk83c60dZKVeMjCNxJZplydB/vUmj4BpArbXWrlZkHe4STMg72aT+XCjybwgX8fqu6vuhX0z0vbgCQ1s3fmjc6YUaP8fUWYA0GqEM1V3EdEvIPUGNXYF8zZwHtRLoYbiRY1aHgq3KvDVH79e7FXHwjxdCghr2xQR0F4kPzb1l5vb1iZvu6gCA/t6fBcCzWEmd8CmdqkX5C0nGW1SB+w7tRR+kH+HSb1AMgrNsS31amC7Dk5nsz2AggI7Bno1K/TEif0fjTJKgE4vkqm684j2VNLMU1OQHR4Za/NIGer+8w7Nk69ek2hVEOl0M+cDUZXAfgbuEkpcplo1DtirNEVeh8m25tOWlQJ2wT3q2Vb3WmHPh4DGNUNiYFPXXy7In2tSB2wXWLYX5Cuw5pvcgSxdrAOh7mAXt9Srge0FmBngwFY1sNWOT/rfJnsA/gtTdeeRMufYz0oBu3qHXFdlGdEugZPMPQXsc02z0Ez5QBQc2FVB2ZkFBtiqBPZd5hYHgbdXA7seme7xxLZOUiHweZkk09uB/UsthR/k32xSB2x6uSwEGvVIaIXLuqRaxjUXuqBlWz0pmgkpvASfgn+CZ8eahDY4sNHq5hD108dk/xblBSAosClPAyob2ulwIG897Ce068uIj562mR4V7OoIUF06b96ngwOi2gY2ONPc5KcBGS/Iu8FkOplqr/6rlZMPNM+SZAYLsOuukAvzS/U1Ywt6tAtoteJFgyPnsJu8mGdBLec6H2aCOUnyqwR26m4tYut2YHvvoNjn+PQ+otVwuluAzPZ+VowDdjVHv2z5u5rZDwR1vmO7xz7bPzVzBuS5fmy2Z5q5EOi8L8JkaeSxEPi+z+QR2O+kO+1FqBE/NWMyF7qI+FQJ7CtJ2v27Hdj72KlwwKaxg2huSxHR6Am5BVQP0GioTz7ohXKp9q04B+zR1ALSQ83kB4IqbeyX4c8qcIFV44CtSmB/yNzkp/kF6u7xcbLJFJiad1AKfB41lxSw72ySXKBJnVT2NYjTEfBV8+MBeAm73ovfTrRBgY3uf9DXlOl2YKde3AXZfNok0RwHMzP6yJuKXT0mmU8+YPuA+RYOyklYJbB7PvJobsHATcPBpaDs+2HUl9rGkMBeatVFYP/jltUxKEPTA+6B57HrDYSYaEIDW/3qPv9uB/Z77FQ4YFNcefUx0eQ2J8j3tqHxmWa+GnLP7/azAwgC2toHNj6lczMEdOqp0ABNO3P7sWOgucSqi4Aprx+6MihHPSE/ZNMbDCL5oYF9P4nPv9uBnRk0weZGDPOIpvKSMvymmO+JpGtUYD9v7oMG6rjGqnPAPAO7d0i+KihHd+9fsJkXEKGBnTe7rxd37GhRRBHRzDJ5BuSvb5spYJ9qvl29Y/d6rkgngd3R5KMqoI7UiGUMsrRKpPSOHwLKEW5i0xcQdQvszDdGsM9o17UTP01NXttcHMibgl3vTr42tmYLynexmfxA0G937KDARvd3qPndlYn7FlZdBuR/FGrEbsCgHPVBa+CmPSDqFtiZSU/Y1Ycd0ivyRRjNu2dfTTqNtGoxwikwE/TY4l6ReWbyA0G/3bFfNPdCoHvIXLoOytYsv9tg7tyWUFDE30jag6FWgQ2PtJ+eAvVEI4UhRKvRVw3Lx/sHWDEO2NWPHa3OId3NzH4g6Lc79mPmXgh0T5rLoIAqoimX8K+tGjsD/uoOPJfNZBDULbBV5gb20x2waZ3mEp++iPhodVGmGxCbpkpEI48wM9KZAoJ+u2Nrsk8p0HlHHn1Aqw+j3wQ1f3oyfpmXFmyayKQ7tdb8ubsNdj2S1aevEcdO7+A/gskLX8fAzowUCuSpvRw8VwSqGeLmByWBPRrJJF1kpnwg6rc7ttbslQKdln9550i3A50bzmdbPRZXWZbyNOXydvgsjKeIuhFXdnXnju5apFfBJ6SpiNRSLcqoY2CfQupdfItd83yOTup9RKM1qBuZWwrYN4Tx7L7yuEDUb4G9h7mXAq0b2SwCUn3d3wG/71mW6lOfcwrYUlMJ2D8Yc/ICXkuqoAkC+utIkv61C2yjzpN34YDs1KuXby1g1othPB87WpgNNVLp7b7DrpdKzfWOgj9PlwLCvgps3LTs6rlWCcVAt8TcCoFOM9Qc2E/OTck8IbD91rIjYNoapoIAzbdg6WQtAd13SZK+dQ1slb2v/excoFOgjhO1beZcoNvPyk5NnygEwr4KbAE/DUuXAt295lIIdD8xF/loxHKaZSlP3U8pYNMIphuNw6QLmfnWBraLYWG7m2ytIGqfCVjbwDbubj99QKAcTYzaKy6X4y7uCUkCcV+9PApWxwtWTC7QCDuZmxfk667img3s30fiHoVs6+UwM9qJTS+x7m7Evhbv+hYNFPa7k68pse0+dQ9sUYNVmX7oUOCvb798Mi6Pbe9Chlzg0Hd3bAHfg2FpLwQSzazzfjOObL34XdRSRloh8/aPTS+HGWDXrL9RJpNuT/bdxWdfM/pyQb6eDr5pp0MhsFWPZu/pmyjlbWKDtPjoo0nJD+eoKRJcRgSc+jKwBfw1rzckuPXNDXcBlLKvJU2XRwID+643JAmy1K6/s6VKA/uD8GNwbXZVbtxevBBqDWUuyNfKHF/ADInAjkl9mnI8C27Aft6XoPRFAX1uwzXZ2NaLZdALfgY49m1gU4QC6bOkrimRB3TqxnsC3m3pG5alPOFqNnNfdMjXy9AvWx5ZkPc81B/Qr6D6tHPPAXmCPpzj/dwY9iEV2G1UP7U+VvkZeBjUQoPUt/tE7IfDzKBPMHDuuzZ2OyhHAyfqQy5d4NsOfBSE+uMofRRKg1YXTJ9tqzwYIx+gb5Bcz27qQidJ/lAO7FxyXFoepnGB1JewOoIKgfoKUimp3DsnoAooQwtZ9Y1kbx1tLB42rQjqXp8yFXg/gGpbK4jcbDy21QPxHFTz4UqoZkNmeVIZ8FEvyM7w61AffdR/gsi8JGJTffpu4R+gjkkfi9Tvjr8fncdr4K0BvBT6/C+APn07b4A+/25RcaBJUPr/lOrHDv6UXIMCcCL1Vf1RUGvtRG0HDbNXAWVG9XDx1FzRfAdxotWXGtBgX11cawztZzdo0KBBgzUEw4b9HxtvAtffJUpfAAAAAElFTkSuQmCC"},"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.

Suite Bar logo is not displayed after applying theme

In SharePoint Online I have detected a bug that prevents the suite bar logo (configured in the o365 tenant admin center) from being rendered after applying a theme with a non-default colour scheme. The logo is not just hidden or not visible; the HTML is no longer rendered to the page at all.

NOTE: This issue has been raised with Microsoft and they have acknowledged it as a bug. They have told is that a fix is planned for June 2015 and should take a couple of weeks to reach all tenants.

Update 06/08/2015

I was advised last week that a fix this issue had officially started rolling out. Today I can confirm that one of our test tenants no longer exhibits this behavior! At the time of writing some of our tenants have not yet received the fix, but it is definitely rolling out and should be with you soon if not already.

When exactly is this occurring?

There is a colour scheme file that nominated as the default for each master page. Only when using this colour scheme in conjunction with the given master page will the logo be present. Specifically, only a colour scheme which is nominated as the default colour scheme will show the suite navigation logo.

This is unrelated to any customisation work and can be observed on any OOTB SharePoint Online publishing site.

The default colour scheme for a given master page is nominated within that master page’s preview file. The preview file is the one which sits alongside the master page in the master page gallery and has the same name name as the master page but with an extension of ‘.preview’ instead of ‘.master’ (e.g., seattle.preview). The first line of the contents of this file identifies the default colour scheme for the master page. When this colour scheme is applied, the theming engine assumes that the CSS is already themed appropriately and does *not* act.

Identifying the default colour scheme for a master page
Identifying the default colour scheme for a master page

I still want the logo on every page, what can we do?

  1. As I have said, a fix is on the way – just wait a few months!
  2. We can inject the logo using JavaScript (or CSS, but we were unable to achieve a CSS only solution which meets our responsive design requirements)
    • It is against best practice
    • This approach is brittle as the suite bar is a living, changing thing – Microsoft is updating it all the time and any changes they make will likely break this functionality.
    • The logo will no longer be configurable via tenant admin settings.
  3. We can create and use multiple master pages – one for every theme colour
    • Partially defeats the purpose of master pages which is to maintain all common pages elements in a single place.
    • Master pages may fall out of synch
    • Adds complexity for support

I want to reproduce this myself, how do I do it?

  1. Create an new site collection in a tenant that has a suite navigation logo configured, using the Publishing Site template
  2. Note that the logo is present
  3. Via Site Settings, “Change the look”
  4. Choose the “Office” composed look
    [Specifically, we want the seattle master page and colour pallette 001]
  5. Change the colour scheme to anything except the default
  6. Apply the look
  7. Note that the logo is no longer present
  8. Re-apply the theme with the default colour scheme and logo will reappear

 

Paul.

Follow documents from search hover panel

There is a somewhat confusing logic behind when the FOLLOW button is displayed on the search results hover panel (a.k.a document preview).

A document hover panel with both the POST and FOLLOW buttons present
A document hover panel with both the POST and FOLLOW buttons present

What I am talking about?

If you are building a solution that relies on the following of documents but you are using Yammer rather than the SharePoint social feed then you may be wondering why, from the search results hover panel, you can follow pages, users, sites, but not most document types.

NB. If you are finding that you can’t following anything, check that web scoped feature ‘Follow Content’ has been activated on each site which contains content you wish to be able to follow.

NB. You can still follow the document types in question by clicking ‘view in library’ and using the library item menu to follow.

In many cases, wanting both POST and FOLLOW doesn’t make a lot sense as a primary reason of following documents is to populate the activity feed which is not available when Yammer is being used as the enterprise social experience. As such, please consider why you want this behaviour at all. In my scenario the user’s list of followed documents is promoted to the home page and bookmarking documents is a key user story.

What is going on?

The search results hover panel is built from a number of display templates which you can read about in more depth here (TechNet) or here (Chris O’Brien) or many other places.

Importantly, there is a display template which defines the common actions (buttons) across the bottom of the hover panel and when to display them. The display template is called Item_CommonHoverPanel_Actions and can be found here:

Site Settings > Master Pages and Page Layouts > Display Templates > Search > Item_CommonHoverPanel_Actions.html

If you inspect this display template you will find an if else block around the rendering of the POST and FOLLOW buttons. The logic can be summarised as:
The POST button is visible if Yammer is enabled and the result type supports it, otherwise the FOLLOW button is visible if the result type supports it, at no time will both buttons be visible.

If you download a copy of the display template HTML file, update it to remove the ‘else’ as in the code snippet below, and then upload it again, you will find the both the POST and FOLLOW buttons will be displayed in the search hover panel when supported. Success!

But is it okay update that file?

The short answer is yes. Take care as this file is used by every hover panel in SharePoint (to my knowledge, there may be some completely unique ones) and so changes could break something that isn’t obvious.

The major risk is that if Microsoft decide to update the hover panel which require them to produce a new version of the display template file (they have done this previously when introducing the POST button). In the case that you have modified this file, then your changes will be lost. This can happen without warning (unless you have a second tenant on first release to catch these issues before they hit production – you should be doing this!).
For very minor updates such as this, and to support non-critical functionality, it may be okay to make these changes and be prepared to re-implement them should Microsoft issue an update.

The alternative is to make a copy of the display template with a new name. This approach means that your changes will not get overridden but it also means that your solution will not get the updates that would otherwise be pushed to this file. We call this ‘customisation tax’ and it is a trade off as to which way you’d rather push changes.
In this particular scenario this latter approach is not very practical as every result type references the existing display template. You would be required to make copies of all the result type display templates that are applicable (possibly a dozen or more), and update the result types themselves to use your new templates. Unless you are bypassing result types and using a single display template for all results, this feels overly complex for such a minor change, but major changes will necessitate the effort.

EDIT: A colleague of mine, Luis Manez, pointed out that with a little JS you can force a custom hover panel to be rendered for all result types. You can read about it (approach one) and some other approaches to associating custom hover panels here (Elio Struyf).

Paul.

Increase Search Result Limit Beyond 50

Content Search Web Parts (CSWPs) and Search Result Web Parts (SRWPs, a.k.a Result Script Web Parts RSWPs) prevent more than 50 results being returned at once. This is true with or without paging enabled. This is true even if you provision the web part using XML. In older versions of SharePoint, it may allow you to enter a row limit greater than 50 but will then default to a mere 10 results.

increaseSearchResultsRowlimit

I’m sure Microsoft has very good UX and technical reasons behind this limitation, however at 100 items I don’t feel that either UX nor performance suffer. I imagine that if using this technique for returning large page sizes (>100) that there is very real potential for bad performance and the UX is most likely appalling.

Importantly – this code should only be considered a learning tool. This code is entirely unsupported and generally just a really bad idea. Please be responsible 🙂

Recognition to Matt Stark who provided this solution. All I have done is rewritten it a bit for safety and I wanted to prefix it with a bit more discussion than it had. Original discussion is here.

The web parts which you want to act upon must be set to load asynchronously (this is not the default mode).
Edit Web Part > Change Query > Settings > Loading Behaviour :: Async option

Edit Web Part > Change Query > Settings > Loading Behaviour :: Async option
Edit Web Part > Change Query > Settings > Loading Behaviour :: Async option

Take note:

  1. There are a few available solutions to this, none of which are much good, and even this one is should be considered a hack and be treated with caution.
  2. I will only discuss this option as it is the best I have come across when considering the limitations of SharePoint Online.
  3. Please only include this code on specific pages using a Content Editor or a Script Editor, and NOT globally.
  4. I have amended the original code such that it only changes the row limit for those web parts which are set to return 50 items exactly.
  5. I haven’t found any issues with this code, however I am using on a page that does nothing but display a list and in a situation where failure has limited impact on the system.
  6. Apparently doesn’t work for anonymous users

And finally the code…

// <script type="text/javascript">
var CC = window.CC || {};
CC.CORE = CC.CORE || {};

CC.CORE.IncreaseSearchResultsMax = (function () {
    "use strict";

    var $ocreate = null;
    var newMaxItems = 100;
    var oldMaxItems = 50; // web part must be set to show this many items

    // on application initialization 
    // steal the global create variable and 
    // intercept calls to create UI widgets.
    Sys.Application.add_init(function() {
        $ocreate = $create;
        $create = updateResultCountCreate;
    });

    // listen to UI widget calls for CBS & DP
    var updateResultCountCreate = function (a,b){
        var ps = Array.prototype.slice.call(arguments, 0);  
        if(a === Srch.ContentBySearch && b.numberOfItems === oldMaxItems) { 
            b.numberOfItems = newMaxItems;
        }
        if(a === Srch.DataProvider && b.resultsPerPage === oldMaxItems) {
            b.resultsPerPage = newMaxItems;
        }
        $ocreate.apply(this,ps);  // apply the original $create method that we stole
    };
    return true;
})();
// </script>

Paul.

Linking to pre-filtered or ‘refined’ search results

Good news – it really is very easy to have links to pre-filtered search pages (search pages with active refiners). This post will give an example of linking to search page with a single active refiner but you should be able to extend the example to more complex scenarios if you wish.

EDIT: I have written a more recent post which provides JavaScript functions to generate these links dynamically

refiner

Cut to the chase

This is what you are looking for, I’ll explain it in depth below:

<Server Relative URL of Search Page>
#Default=%7B%22k%22:%22%22,%22r%22:%5B%7B%22n%22:%22
<Managed Property Name>
%22,%22t%22:%5B%22%5C%22ǂǂ
<HEX Encoded Managed Property Value>
%5C%22%22%5D,%22o%22:%22OR%22,%22k%22:false%7D%5D%7D

e.g.
/search/Pages/results.aspx
#Default=%7B%22k%22:%22%22,%22r%22:%5B%7B%22n%22:%22
RefinableString08
%22,%22t%22:%5B%22%5C%22ǂǂ
506f6c696379
%5C%22%22%5D,%22o%22:%22OR%22,%22k%22:false%7D%5D%7D

You can quickly perform HEX encoding using an online tool such as this.

(The above URLs have been separated on to new lines for readability but should not contain any white space)

Breaking it down

As you have probably already noticed, by applying a filter to a search results page you are appending a hash (or anchor) to the query string. This value is a URL encoded JSON object. Once decoded it looks something like this:

You can quickly perform URL decoding using an online tool such as this.

Importantly for our purposes we can see the name of the managed property on which we are refining “RefinableString08”.

The value is a little more obfuscated. The value ‘5265706f7274’ is in fact the HEX encoded value which we want to refine on. It is vital the “t” property is set to this value prepended by the two “ǂ” characters. The “m” property appears to define a mapping between the HEX encoded value and original value but it does not appear to be necessary.

Reducing the length to fit such that it can be used as a ‘Simple Link’ in a navigation term

A navigation term only supports a limited number of characters in its ‘simple link’ field, approximately 260. This means that the full encoded JS object as above will breach this limit if the managed property value is greater than about 8 characters (if using a ‘RefinableStringXX’ managed property). Not great. However, it turns out that the “m” property of the object is optional, at least when performing simple refinement as we are here. The example I provided at the top of this post has this property excluded in order to reduce it’s length.

 

Paul.

 

Recreate the Visual Studio Create GUID tool

Depending on the default settings under which you are running Visual Studio the Create GUID tool may not be present.
(Tools > Create GUID).

Create GUID tool in top menu
Create GUID tool in top menu

It is very easy to recreate. Simply click Tools > External Tools…, then Add. Populate the tool properties as such:

  • Title: Create &GUID
  • Command: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\guidgen.exe
  • Arguments: *leave this blank*
  • Initial Directory: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\

Click OK.

The 'Create External Tool' dialog
The ‘Create External Tool’ dialog

Paul.