Monday, January 29, 2018

Internal details of setting Access request settings for Sharepoint sites

In Sharepoint you may configure Access request settings which will allow users to ask for access to the site from appropriate responsible person (I intentially don’t tell site owner here because recipient of access requests may be different person – see below). In order to do it first of all you need to configure outgoing email settings in Central administration. Access request settings for web site may be configured from Site settings > Site permissions > Access request settings:

When you click this link Sharepoint loads setrqacc.aspx into modal window. If we will check its codebehind class Microsoft.SharePoint.ApplicationPages.SetRequestAccess we will see that it first checks SPWebApplication.RequestAccessEnabled internal property:

In this property it checks another property IsEmailServerSet:

which in turn checks outgoing email server address:

That’s why setting outgoing email settings should be done before to configure Access request settings.

Now let’s check SetRequestAccess.OnLoad method again and see how “Allow access requests” checkbox is initialized with checked/unckecked values. As it is shown above OnLoad method calls ToggleSelectedAndSetObjectType method:

and the last one checks “Allow access requests” checkbox when SPWeb.RequestAccessEnabled property set to true. Let’s check code of SPWeb.RequestAccessEnabled property:

I.e. it is readonly property which returns true only when Access request email is set, i.e. other property SPWeb.RequestAccessEmail. So basically in order to enable Access requests you need to set SPWeb.RequestAccessEmail for your web site.

The problem however is that Sharepoint for some sites sets SPWeb.RequestAccessEmail to default value – see first picture in this post. There is internal method SPWeb.EnableAccessRequestsIfNeeded where it is done:

Here it’s code as it doesn’t fit to post column’s width:

   2: private void EnableAccessRequestsIfNeeded()
   3: {
   4:     if (string.IsNullOrWhiteSpace(this.RequestAccessEmail) &&
   5:         this.HasUniqueRoleAssignments && this.WebTemplateId != 3
   6:         && this.WebTemplateId != 16 && this.WebTemplateId != 17 &&
   7:         this.WebTemplateId != 18 && this.Site.WebApplication.RequestAccessEnabled)
   8:     {
   9:         this.RequestAccessEmail = "";
  10:     }
  11: }

So for root webs of new site collections which have HasUniqueRoleAssignments = true and which use web template id except 3, 16, 17, 18 (and of course if SPWebApplication.RequestAccessEnabled is set to true – see above) Sharepoint will set SPWeb.RequestAccessEmail to default value which will mean that Access requests will be enabled there by default. In the following forum post Access Requests recipient by Default it is mentioned that in this case, i.e. when default email address is used, SPWeb.Site.Owner.Email will be used for sending access requests, although I didn’t check it by myself.

And one more thing: if you are working with CSOM note that Web.RequestAccessEmail property is available there only starting with version 16.1.4727.1200 onwards. Basically it means that it is not possible to change Access requests settings by CSOM for Sharepoint 2013 on-premise. It is still possible though via basic server object model.

Thursday, January 25, 2018

OfficeDevPnP classes and methods reference

Office 365 Developers Patterns and Practices library contains many useful methods for Sharepoint Online development. I would like to have some kind of reference page which would contain all methods from this doclib in one place so I can search them by Ctrl-F without pagination. In order to do such reference I used NDepend tool and built report from OfficeDevPnP.Core.dll v.2.22.1801.0 which is currently latest version for Sharepoint Online (note that for on-premise there is separate assembly). NDepend has useful feature called Code Query Linq (CQLinq) which allows you to make queries to the code in loaded assemblies, i.e. work with code as it would be data. E.g. for getting all methods with their parent classes I used the following Linq query:

   1: from m in Application.Methods
   2: let t = m.ParentType
   3: where !t.IsGeneratedByCompiler && !m.IsClassConstructor && !m.IsConstructor
   4: select new { m.ParentType, m }

After that exported report to Excel where added some formatting and saved to GitHub gist:

You may use this gist as your reference for OfficeDevPnP as well.

Thursday, January 18, 2018

Enable possibility to download json files in Sharepoint

Sometime you may need to enable possibility to download json file types in Sharepoint (e.g. we had a case when it was needed for integration purposes). By default if you will put this file e.g. into /_layouts folder and try to access json file in context of Sharepoint site:

you will get the following error:

The page must have a <%@ webservice class="MyNamespace.MyClass" ... %> directive.
Stack trace:    at System.Web.UI.SimpleWebHandlerParser.ParseReader
   at System.Web.UI.SimpleWebHandlerParser.Parse(ICollection
   at System.Web.Compilation.SimpleHandlerBuildProvider.get_CodeCompilerTy
   at System.Web.Compilation.BuildProvider.GetCompilerTypeFromBuildProvider
(BuildProvider buildProvider)
   at System.Web.Compilation.BuildProvidersCompiler.ProcessBuildProviders()
   at System.Web.Compilation.BuildProvidersCompiler.PerformBuild()
   at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath
   at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal
(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean
   at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert
(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean
allowCrossApp, Boolean allowBuildInPrecompile)
   at System.Web.UI.WebServiceParser.GetCompiledType(String inputFile,
HttpContext context)
   at System.Web.Services.Protocols.WebServiceHandlerFactory.GetHandler
(HttpContext context, String verb, String url, String filePath)
   at System.Web.Script.Services.ScriptHandlerFactory.GetHandler
(HttpContext context, String requestType, String url, String pathTranslated)
   at System.Web.HttpApplication.MaterializeHandlerExecutionStep.System.Web
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously)

In order to enable possibility to download json file from Sharepoint we need to do the following:

1. Create new application sub folder under your Sharepoint site in IIS manager. If you already have virtual folder you need to convert it to application also in IIS manager. Separate folder is needed in order to not affect whole Sharepoint, but only one exact url:

2. Add web.config to this folder and add mime type record for json extension under system.webServer > staticContent:

   1: <configuration>
   2:    <system.webServer>
   3:       <staticContent>
   4:          <mimeMap fileExtension=".json" mimeType="application/json" />
   5:       </staticContent>
   6:    </system.webServer>
   7: </configuration>

3. In IIS manager open handler mappings for your application sub folder and remove handler for *.json (that's why it is important to use separate application sub folder in IIS – in case of app this change will only affect this sub folder, not whole Sharepoint site. I.e. there won’t be side effects with this approach).

Now if we try to access its content will be successfully downloaded.