Recently a user complained, that he is not able to open office documents from the SharePoint portal of the company when working at home. He attached a screenshot of the error message in the browser to his mail. From this screenshot was it obvious, that there is an issue with the accessibility of the Office Web Applications server (OWA, also known as WAC), see the word wac in the address on the browser screen or WopiFrame.aspx in the address bar.
As it turned out, the WAC-server of the company has not been published externally via the firewall, but it was on purpose. Users should have been able to work with documents using their locally installed Office applications.
As you might know, you can configure the behavior, if document would be opened in browser or in the Office client application instead on either the site collection or on the document library level (see details here). The documents the user complained about were located in a library with the setting “Use the server default (Open in the client application)”, but it has not helped, when we changed it to “Open in the client application” explicitly.
It was really curious, but after a little while it turned out, that he wanted to open the document not from the library, but from a search result. At least a step further to the solution, have we thought.
You should know, that the behavior, if the Office documents get opened in the client application or in the browser is independent from the site collection level settings as well as from the document library settings. There is a (in my personal opinion pretty hidden) Preference link at the bottom of the search results page:
On this page the users can configure their own preferences, among others, if they would like to open the Office documents in the client application or in the browser:
It’s a cool option to enable users to decide which way they prefer, although it is pretty inconsistent with the other options (available for the administrators) we mentioned earlier. But there is an even bigger issue (at least, for me) with that. There is (as far as I know) no option / UI for administrators to query the value configured for a user, not to mention, how to change it remotely, without end user interaction.
Although it might have been the easiest choice to ask the user, which value he has configured for himself , I’m not the man of easy options if there might be a programmatic approach as well and a chance to learn something new. So let’s see, what I’ve learned.
The user preferences regarding the search are available via the Microsoft.Office.Server.Search.Administration.UserPreference class. If you need the user preferences from the current SPContext (e.g. for the current user), you can use either the static GetUserPreference() method or the other static overload GetUserPreference(bool lookupFromCache). If, however, you need the preference for another user, you can inject it via the static GetUserPreference(bool lookupFromCache, SPContext context) method.
For example, the DisplayPreference method below displays a few of the available preferences from a context it receives as parameter:
- private void DisplayPreference(SPContext ctx)
- {
- var user = ctx.Web.CurrentUser;
- Console.WriteLine("Reading preferences for '{0}'", user.LoginName);
- var userPref = UserPreference.GetUserPreference(false, ctx);
- Console.WriteLine("ShowPrequerySuggestion: {0}", userPref.IsSettingEnabled(UserPreference.Settings.ShowPrequerySuggestion));
- Console.WriteLine("ShowPersonalSuggestions: {0}", userPref.IsSettingEnabled(UserPreference.Settings.ShowPersonalSuggestions));
- Console.WriteLine("OpenDocumentsInClient: {0}", userPref.IsSettingEnabled(UserPreference.Settings.OpenDocumentsInClient));
- }
The following code snippet (taken from a console application) invokes the DisplayPreference method first to display the preferences of the current user, then again to display the preferences of an impersonated user:
- using (SPSite site = new SPSite(url))
- {
- using (SPWeb web = site.OpenWeb())
- {
- var ctx = SPContext.GetContext(web);
- DisplayPreference(ctx);
- var user = web.EnsureUser(@"i:0#.w|domain\user");
- SPSecurity.RunWithElevatedPrivileges(
- () =>
- {
- using (SPSite impSite = new SPSite(url, user.UserToken))
- using (SPWeb impWeb = impSite.OpenWeb())
- {
- var impCtx = SPContext.GetContext(impWeb);
- DisplayPreference(impCtx);
- }
- });
- }
- }
Of course, if your code runs in a SharePoint process, you can get the context as SPContext.Current as well for the current user.
The same information is available via PowerShell either. For example, displaying preferences for the current user:
- $web = Get-SPWeb $url
- $ctx = [Microsoft.SharePoint.SPContext]::GetContext($web)
- # shortcut for UserPreference
- $up = [Microsoft.Office.Server.Search.Administration.UserPreference]
- # shortcut for the nested class Settings in UserPreference
- $ups = [Microsoft.Office.Server.Search.Administration.UserPreference+Settings]
- $pref = $up::GetUserPreference($false, $ctx)
- $pref.IsSettingEnabled($ups::ShowPrequerySuggestion)
- $pref.IsSettingEnabled($ups::ShowPersonalSuggestions)
- $pref.IsSettingEnabled($ups::OpenDocumentsInClient)
If you need the preferences of another user, you should impersonate it first as described here. After the impersonation, the code is pretty the same as earlier:
- $userName = 'i:0#.w|domain\user'
- $web = Get-SPWeb $url
- $user = $web.EnsureUser($userName)
- $userToken = $user.UserToken
- $impersonatedSite = New-Object Microsoft.SharePoint.SPSite($url, $userToken)
- $ctx = [Microsoft.SharePoint.SPContext]::GetContext($impersonatedSite.RootWeb)
- # shortcut for UserPreference
- $up = [Microsoft.Office.Server.Search.Administration.UserPreference]
- # shortcut for the nested class Settings in UserPreference
- $ups = [Microsoft.Office.Server.Search.Administration.UserPreference+Settings]
- $pref = $up::GetUserPreference($false, $ctx)
- $pref.IsSettingEnabled($ups::ShowPrequerySuggestion)
- $pref.IsSettingEnabled($ups::ShowPersonalSuggestions)
- $pref.IsSettingEnabled($ups::OpenDocumentsInClient)
Using this code we were able to detected that the complaining user has really the wrong preference (OpenDocumentsInClient was false). Now we had two choices: either to call the user, and ask him to change the preference, or to find a solution, how it would be possible to change it from code on behalf of the user remotely. Of course, this time we didn’t want to change the preferences without the explicit permission of the user, so took option 1, but I show you in my next post, how you could do it from code.