maandag, oktober 14, 2013

First experiences with SharePoint 2013 item templates

Microsoft has done a great job with pushing their search technology a step further into the central arena of creating SharePoint applications:

  • Using the content by search webpart one can now show result sets across site collections whereas the content by query webpart was limited to the scope of only one site collection;
  • Using display templates in order to create customized visual representations – just by editing html templates right from your favorite html design tool.

Some caveats though that I would like to express, hoping to influence Microsoft product development:

  • Web Part configuration – the configuration of all the various content search webparts across your solution can become quite cumbersome, i.e. 1) Browse to the page, 2) Toggle Edit mode, 3) Edit the webpart, 4) Edit all the various settings of the content by search webpart, 5) Save the webpart settings, 6) Save the page. I am looking forward to a tool that will make my live easier;
  • Where is the js bundle? These display templates are automatically compiled into JavaScript files which are supplied to the user’s browser. If you have a lot of them on one page, all of these .js-files are essentially supplied separately! It would be great to have a bundler in place, that serves all JavaScript code at once;
  • Selecting the right metadata – your display template needs to know what metadata fields are available to render. SharePoint currently does not have a tool on board to help you in catching available metadata. You need to traverse from content type internal fields names into the search schema yourself to discover the metadata field names!
  • Separating html from JavaScript – Microsoft promises great separation of jobs between web designer and developer. Using display templates the web designer should be able to create presentations with their favorite html tooling. Unfortunately a display template still contains loads of JavaScript and conditional logic! We are hoping on improvements in the future;
  • Server-side rendering – with the new JavaScript based display template model, customized rendering will be taking place solely client-side. Is this good or bad? Don’t get me wrong, I really like the asynchronous processing, but I am not confortable with doing everything client-side.

woensdag, oktober 09, 2013

Troubleshooting restoring a site collection

The following applies to SharePoint 2013 RTM and potentially to SharePoint 2010, but was not tested.

Ever tried to backup and restore a site collection with PowerShell using a least privilege SPRestore account approach and bumped into some fatal errors? As a starter you should have followed the instructions that are documented on TechNet:

Restore site collections in SharePoint 2013
http://technet.microsoft.com/en-us/library/ee748655.aspx

Apparently this documention is not completely sufficient, as you bumped in some fatal errors and are seeking for help. In this blog I would like to discuss two error messages and show you how to address them.

Error 1: access denied

In PowerShell:

02/26/2013 15:38:27.95 PowerShell.exe (0x2B28) 0x2B78 SharePoint Foundation General ai1wu Medium System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)), StackTrace: at Microsoft.SharePoint.SPSite.Restore(String filename, Boolean isADMode, Boolean& readOnlyMode, Boolean& hadWriteLock) at Microsoft.SharePoint.Administration.​SPSiteCollection.Restore(String strSiteUrl, String strFilename, Boolean bOverwrite, Boolean bGradualDelete, Boolean hostHeaderAsSiteName, Boolean preserveSiteId) at Microsoft.SharePoint.PowerShell.SPCmdletRestoreSite.​InternalProcessRecord() at Microsoft.SharePoint.PowerShell.SPCmdlet.ProcessRecord() at System.Management.Automation.CommandProcessor.ProcessRecord() at System.Management.Automation.CommandProcessorBase.DoExecute() at System.Management.Automation.Internal.PipelineProcessor.Syn... 615fe450-5cc1-4608-a50d-edae5cd0d862

In ULS:

System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)), StackTrace: at Microsoft.SharePoint.SPSite.Restore(String filename, Boolean isADMode, Boolean& readOnlyMode, Boolean& hadWriteLock) at Microsoft.SharePoint.Administration.SPSiteCollection.Restore(String strSiteUrl, String strFilename, Boolean bOverwrite, Boolean bGradualDelete, Boolean hostHeaderAsSiteName, Boolean preserveSiteId) 

Solution to error 1

After some debugging we found out that the account additionally needs rights on the webapp. So ultimately one should provision an account using the following procedure:

  • Create the user SPRestore in Active Directory
  • Add the user to the local administrators group
  • Execute the Add-SPShellAdmin PowerShell command using your SPFarm account
  • Additionally execute the following PowerShell command (this is not documented within the TechNet article):
    $w = Get-SPWebApplication -identity $webapp
    $w.GrantAccessToProcessIdentity("SHAREPOINT2013\SPRestore")
    This action is equal to grant full control on the webapp via central administration. By the way, strangely, tests show that only read access is sufficient.
  • Using SQL Server Management Studio:
    Grant the SPRestore user the DB_Owner DB role of the relevant content database
    Grant SPRestore the fixed server role: securityadmin. Also strangely, although documented in the TechNet article, tests show that this latter grant is not required.

Error 2: no content database available

As you most probably already have experienced, a correctly provisioned account can still lead to the following fatal error:

In PowerShell:

Restore-SPSite : The operation that you are attempting to perform cannot be completed successfully. No content databases in the web application were available to store your site collection. The existing content databases may have reached the maximum number of site collections, or be set to read-only, or be offline, or may already contain a copy of this site collection. Create another content database for the Web application and then try the operation again.

In ULS:

System.InvalidOperationException: The operation that you are attempting to perform cannot be completed successfully. No content databases in the web application were available to store your site collection. The existing content databases may have reached the maximum number of site collections, or be set to read-only, or be offline, or may already contain a copy of this site collection. Create another content database for the Web application and then try the operation again. at Microsoft.SharePoint.Administration.SPContentDatabaseCollection​.FindBestContentDatabaseForSiteCreation(IEnumerable`1 contentDatabases, Guid siteIdToAvoid, Guid webIdToAvoid, SPContentDatabase database, SPContentDatabase databaseTheSiteWillBeDeletedFrom) at Microsoft.SharePoint.Administration.SPContentDatabaseCollection​.FindBestContentDatabaseForSiteCreation(SPSiteCreationParameters siteCreationParameters, Guid siteIdToAvoid, Guid webIdToAvoid, SPContentDatabase database, SPContentDatabase databaseTheSiteWillBeDeletedFrom) at Microsoft.SharePoint.Administration.SPSiteCollection.Restore(String strSiteUrl, String strFilename, Boolean bOverwrite, Boolean bGradualDelete, Boolean hostHeaderAsSiteName, Boolean preserveSiteId) at Microsoft.SharePoint.PowerShell.SPCmdletRestoreSite​.InternalProcessRecord() at Microsoft.SharePoint.PowerShell.SPCmdlet.ProcessRecord()

Solution to error 2

After some research (decompilation of the relevant methods within the SharePoint assemblies) it looks like SPSiteCollection.GetContentDatabase contains a bug. I created two c# snippets to simulate the innerworkings of the PowerShell command:

string SiteURL= "http://spdev/";
string BackupFile = @"c:\backup\backup.bak";
SPWebApplication webapp = SPWebApplication.Lookup(new Uri(SiteURL));
SPSiteCollection mySiteCols = webapp.Sites;
mySiteCols.Restore(SiteURL, BackupFile, true);

results in the exception.

string DatabaseServerName = "spdatabaseserver";
string ContentDatabaseName = "contentdb";
SPContentDatabase db= Support.GetContentDatabase(webapp, DatabaseServerName, ContentDatabaseName);
db.Sites.Restore(SiteURL, BackupFile, true);

does not result in the exception. This brings us to the conclusion that one should always specify database server name, and content database name. Instead of:

Restore-SPSite -Identity $SiteURL -Path $BackupFile –Force

Always use:

Restore-SPSite $SiteURL -path $BackupFile -force -databaseserver $DatabaseServerName -databasename $ContentDatabaseName

I hope this blog is of any help to all of you out there doing great SharePoint work. Please let me know if you need any further assistance.