Retention Policies in Office 365 

I had a requirement to configure a mechanism within Office 365 to allow pages on an intranet to expire after a set amount of time.  When a page has expired the content owner is alerted and a task is assigned to review the page content. 

A number of content types have been designed and each content type can have an information management policy assigned to indicate the retention period. 

To configure retention in Office 365: 

  1. Create a content type the usual way
  2. Connect to the site using SharePoint Designer and create a workflow.  Select the SharePoint 2010 workflow engine as this will allow you to connect the workflow to a content type.
  3. Modify the content type through SharePoint (Site Settings –> Content Types) and select ‘Workflow Settings’
  4. This should allow you to select the workflow created in step 2.

   addworkflow

Now the content type is ready, and the workflow is assigned to the content type – we now need to configure the retention policy:

  1. Select ‘Information Management Policies Settings’ from the content type page
  2. Configure a retention policy
  3. Configure the expiration policy
  4. Configure the action to start a workflow and select the workflow created earlier.

retention overview

retentiondetail

Note: After testing in Office 365, we cannot manually start the retention timer job to iterate through the expired items – the timer job runs every Sunday at around 08:30 (in my tenant at least) – makes testing quite difficult, so any testing should be completed in an on-prem environment so the timer job can be manually executed.

Office 365 authentication prompt when opening documents

In our environment we are using Office 365, we configured ADFS / ADFS Proxies / and Dirsync to manage SSO.

We have constructed smart links to ensure users are automatically signed into SharePoint when using their work laptop and Internet Explorer – if they are using a difference device / browser then they are directed to the corporate login page.

We experienced an issue where customers would be requested to sign into Word / Excel / PowerPoint when opening files directly from SharePoint.

This was resolved my modifying the smart links to contain 

LoginOptions%3D1

 

Example smart link:

https://sts.contoso.com/adfs/ls/?wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline&wctx=MEST%3D0%26LoginOptions%3D1%26wa%3Dwsignin1.0%26rpsnv%3D2%26ver%3D6.1.6206.0%26wp%3DMCMBI%26wreply%3Dhttps:%252F%252Fportal.microsoftonline.com%252FDefault.aspx%26lc%3D1033%26id%3D271345

more info on smart links

http://community.office365.com/en-us/w/sso/using-smart-links-or-idp-initiated-authentication-with-office-365.aspx

SharePoint 2013 Search (Office 365)

I was hunting for information on query suggestions for Office 365….query suggestions are handled in two ways:

1: You can upload your own query suggestions through the admin portal

2: SharePoint will dynamical build a list of query suggestions based on previous searches and click throughs:

SharePoint automatically creates a query suggestion when users have clicked a search result for a query at least six times. For example, if users have entered the query word “coffee” and then clicked on a search result six times, then “coffee” automatically becomes a query suggestion.

Source:

http://blogs.technet.com/b/tothesharepoint/archive/2013/12/05/create-and-import-query-suggestions-in-sharepoint-2013.aspx

 

Remove accounts from people search

Recently came across an issue where there were 3 “David Hendry’s” appearing within people search on SharePoint 2013 / Office 365.  So if a user searched for me, they would see 3 ‘identical’ people to choose from.

The accounts were:

* My main domain account
* My domain admin account
* My cloud admin account

The requirement was to hide cloud admin and domain admin accounts from the search results.

The resolution was to edit the People Search Core Results web part…

I modified the query (shown below) to include the domain name for internal accounts, remove any accounts containing adm_ and onmicrosoft.com.  

query

 

SharePoint 2013 – Getting Content Type by Name – CSOM

Following scenario is using PowerShell and the Client Side Object Model..

Task: Get a specific Content Type by name rather than ID – as we don’t know what the ID is!

### ASSUMES CONNECTION TO CLIENT CONTEXT - $CTX ###
# Get the web context
$web = $ctx.Web
$ctx.Load($web)
$ctx.ExecuteQuery(
# Gets a collection of available content types
$contentTypesColl = $web.AvailableContentTypes
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$listLoad = $load.MakeGenericMethod([Microsoft.SharePoint.Client.ContentTypeCollection])
$listLoad.Invoke($ctx,@($contentTypesColl,$null))
$ctx.ExecuteQuery()
# Iterate through the content types matching the name
# Once match is found, save content type ID to a variable
$LegalDocId = 0;
foreach($ctype in $contentTypesColl)
{
$i = 0
if($ctype[$i].Name -eq "Legal Document")
{
Write-Host $ctype[$i].Name -ForegroundColor Cyan
$LegalDocId = $ctype[$i].Id
break;
}

$i++
}
# Once content type ID is found, apply content type to a document library
if($LegalDocId -ne '0')
{
# Get the library
$lib = $web.Lists.GetByTitle("Documents")
$ctx.Load($lib)

# Enable content types
$lib.ContentTypesEnabled = $true
$lib.Update()
# Apply the content type
$contentType = $web.ContentTypes.GetById($LegalDocId.ToString())
$ctx.Load($contentType)
Add-LogMessage -message "Adding content type to library"
$ctx.Load($lib.ContentTypes.AddExistingContentType($web.ContentTypes.GetById($LegalDocId.ToString())))

# Update the library
$lib.Update()
$ctx.ExecuteQuery()
}

Error when using Query Builder

I had a minor issue when putting together a result source within my Search Service Application…

My issue was that the ‘Search Results Preview’ wasn’t working, in my case this was an easy fix.

Solution, Enable the ‘Search Server Web parts and Template’ on the Site Collection level within Central Admin.

Search feature for query builder
Search feature for query builder

Issues creating the search service app in SharePoint 2013

Quick post…

I had an issue creating the Search Service Application in SharePoint 2013 – my environment is running the baseline 2012 March CU.

The error I was seeing was:

Unable to retrieve topology component health states. This may be because the admin component is not up and running.

I’m sure there are many reasons for seeing this error, I checked all the usual suspects around permissions, firewall etc.

In my case, it was because I had modified the NodeRunner configuration file to prevent the amount of memory being utilised by this process.

This file can be found in the following location:

C:\Program Files\Microsoft Office Servers\15.0\Search\Runtime\1.0\noderunner.exe.config

I changed the following in the config file:

<!– Settings enforced by the node runner itself. –>
<!– These settings can also be set using command line arguments with the same name. –>
<nodeRunnerSettings memoryLimitMegabytes=”500” />

As default, memoryLimitMegabytes is set to ‘0’ – unlimted…not so good on a below spec SharePoint DEV system.

My resolution was to restore the original value of ‘0’ – and to recreate the search service app – which worked perfectly.

Manually creating ICS files and sending from SharePoint

In a previous post I created Outlook appointments using Microsoft.Office.Interop, a limitation of using this method means MS Outlook is required to be installed on the server.

To avoid this limitation it’s possible to create an Outlook appointment manually and send as an attachment, you still have full control over the content and behaviour of the appointment file, known as a .ICS file.

There are various examples on the web of how to do this, but I struggled to find one that met my exact requirements, my requirements were:

  • Dynamically populate the ICS file with content from a custom webpart within SharePoint.
  • Send the email with attachment using the configuration stored within SharePoint (Outgoing SMTP Server, sender address).
  • Ability for the recipient to accept the invitation, to enable the employee calendar to be updated, with no meeting organiser.

What the code does… Creates a new instance of MailMessage, specifics the SMTP Server, Sender Address, and a couple of other properties.
P.S. I’m not managing garbage collection within this area of code..Also the variable ‘appointment’ is a class that I created to store the variables from the SharePoint webpart.

MailMessage messageContent = new MailMessage();
SPSite site = SPContext.Current.Site;
SPWeb web = site.OpenWeb();
string senderAddress = web.Site.WebApplication.OutboundMailSenderAddress;
string smtpServer = web.Site.WebApplication.OutboundMailServiceInstance.Server.Address;
messageContent.From = new MailAddress(web.Site.WebApplication.OutboundMailSenderAddress);
messageContent.To.Add(new MailAddress("appointment.recipient");
messageContent.Subject = appointment.Subject;
messageContent.Body = appointment.Body;

The next part builds the ICS file as a string. I found these properties by sending meeting invitations to my Gmail account and opening the appointment file in Notepad to understand what parameters I required.

StringBuilder icsBuilder = new StringBuilder();
            icsBuilder.AppendLine("BEGIN:VCALENDAR");
            icsBuilder.AppendLine("VERSION:2.0");
            icsBuilder.AppendLine("PRODID:-//Schedule a Meeting");
            icsBuilder.AppendLine("METHOD:REQUEST");
            icsBuilder.AppendLine("BEGIN:VEVENT");
            icsBuilder.AppendLine(string.Format("DTSAMP:{0:yyyyMMddTHHmmssZ}", DateTime.Now));
            icsBuilder.AppendLine(string.Format("DTSTART:{0:yyyyMMddTHHmmssZ}", appointment.StartDateTime));
            icsBuilder.AppendLine(string.Format("DTEND:{0:yyyyMMddTHHmmssZ}", appointment.EndDateTime));
            icsBuilder.AppendLine("LOCATION:None");
            icsBuilder.AppendLine(string.Format("UID:{0}", Guid.NewGuid()));
            icsBuilder.AppendLine(string.Format("DESCRIPTION", messageContent.Bcc));
            icsBuilder.AppendLine(string.Format("SUMMARY:{0}", messageContent.Subject));
            icsBuilder.AppendLine(string.Format("ORANIZER:MAILTO:{0}", messageContent.From.Address));
if(!string.IsNullOrEmpty(appointment.Body))
icsBuilder.AppendLine(string.Format("X-ALT-DESC:FMTTYPE=text/html:{0}", messageContent.Body));

I couldn’t use the send mail functionality available in SPUtility as I was unable to add attachments, there may be better ways of sending the actually email with the attachments.

Below, I’m using the standard System.Net.Mail functionality:

System.Net.Mail.SmtpClient smtpClient = new System.Net.Mail.SmtpClient();
smtpClient.Host = smtpServer;
smtpClient.UseDefaultCredentials = true;
System.Net.Mime.ContentType contentType = new System.Net.Mime.ContentType("text/calendar");
contentType.Parameters.Add("method", "REQUEST");
contentType.Parameters.Add("name", "MeetingAppointment.isc");
AlternateView view = AlternateView.CreateAlternateViewFromString(icsBuilder.ToString(), contentType);
messageContent.AlternateViews.Add(view);
smtpClient.Send(messageContent);

Converting Times (UTC to Local) – SharePoint

Recently came across a scenario using SharePoint 2010, Duet, and SAP. The scenario being when submitting data to SAP from SharePoint any Date/Time fields would be stored in SAP using the local time of the user whom submitted the data. However when the data is sent back to SharePoint, the Netweaver layer converts any Date/Time fields to UTC time.

After a little research found some very useful information on MSDN that handles the conversion of UTC time to local time based on the regional settings of the SPWeb.

Two useful methods included in the SPTimeZone class:

Examples of how to implement the methods are available on MSDN

SPTimeZone Class: http://msdn.microsoft.com/en-us/library/ms464064