Tag Archives: SharePoint

NodeJS Development Environment w/SharePoint Framework Support

Unless you’ve been hiding under a rock recently (or not interested in SharePoint development), you’ve probably heard/read the recent announcement about the preview release of the SharePoint Framework. You can read the announcement here:

http://dev.office.com/blogs/sharepoint-framework-developer-preview-release

The new SPFx adopts client-side development using Typescript (a superset language of JavaScript) and uses tools born from NodeJS development – such as Gulp and Yeoman. The aim of this post is not to go into the specifics of these tools, besides, there’s lots of information on the Internet.

If you’ve made the leap into client-side development (for SharePoint or otherwise) – congratulations and welcome to the new era of software development. Those of you embarking on the learning curve will soon learn that client-side development (and by extension SPFx development) requires installation of various tools for your development arsenal. The days of just installing a single IDE are fading away. At this point I shall mention that those die hard Visual Studio folks can develop NodeJS and SPFx projects with their IDE. However, you still need NodeJS and dependent modules installed to develop for SPFX. The following article details the steps:

https://github.com/SharePoint/sp-dev-docs/wiki

Like many JavaScript and Typescript developers before me, I have opted for the platform independent tools, using Visual Studio Code. VSCode is a lightweight code editor that embraces client-side and NodeJS development and runs on Windows, OSX and Linux (you can even run it on a Raspberry PI). Just as with it’s big brother, Visual Studio Code works with additional software to constitute a true development environment – alone it’s really just a JavaScript/Typescript editor. I’ll refer you to the previously mentioned article that speaks to installing all the necessary components for SPFx development.

By now, you’re probably thinking “I have to install Visual Studio Code or Visual Studio 2015, NodeJS, Yeoman, Gulp, Windows Build Tools, yada, yada, yada, just to get a development environment up and running?”. The short answer is “yes”. Luckily for you (those of you on Windows at least), I have created a PowerShell script that downloads all the tools and dependencies for you, available at the following location:

https://github.com/robgarrett/Study/blob/master/Install-JSDev.ps1

My script downloads the following binaries and installs them:

  • Visual Studio Code
  • NodeJS LTS
  • Windows GIT

After installation of the binaries, the script uses the Node Package Manager (NPM) to install:

  • Windows-Build-Tools (includes an installation of Python)
  • Yeoman
  • Gulp
  • The SPFx Yeoman Generator (this creates SPFx scaffolding)
  • Typescript 2.0

Typescript 2.0 isn’t necessarily required for SPFx development (I believe TSv1.0 installed as a dependency of one of the other packages), but Typescript is coming to stay so might as well get used to the next version.

Depending on the performance of your development machine and your Internet, the script can take some time installing all the necessary packages. So grab a coffee and let it do its stuff.

Finally, you’re ready to start developing. If you’re ready to dive into SPFx Web Part development you can create your first web-part using the instructions at the following location:

https://github.com/robgarrett/Study/blob/master/Install-JSDev.ps1

FYI – the @Microsoft/Generator-SharePoint downloads a ton of modules and it can take an absolute age. It might seem like a lot of waiting around to develop your first SPFx web part, but SPFx and the workbench rely on lots of modules. For subsequent projects you can always make a copy of the default web-part scaffolding (directory structure and files) save generating from scratch. At the very least the node_modules folder is good to keep because it contains all dependent NodeJS projects and libraries.

So, that’s it. Sorry, if you’re on OSX or Linux – you’ll have to download to the installs per the article instructions until I or someone else creates a bash script to do the same as my PowerShell script (note: PowerShell now runs on Linux, but my script is Windows specific). Hey, at least you seldom set up your development environment from scratch.

SharePoint Crawling User Profiles (SPS3://) – Access Denied w/o HTTP

I stumbled across an interesting issue with People Search in SharePoint 2016. I was attempting to crawl the user profile store with URL: sps3://server-name and getting Access Denied in the crawl log. I checked the Administrators for the User Profile Service in Manage Service Applications and confirmed my default content access account (crawl account) had access to Retrieve People Data for Search Crawlers (see here).

Looking at the ULS I noticed errors about missing Alternate Access Mappings for an HTTP address, before seeing the Access Denied error. This caught my eye because I’ve configured my collaboration web application and my-site host as HTTPS.
For kicks, I added an IIS binding for HTTP://SERVER-NAME and added an AAM for the server name on HTTP, alongside my HTTPS FQDN. Lo-and-behold, after starting a full crawl the log reported successes for people data.

So, it appears that SharePoint takes the URL sps3://server-name and converts it to http://server-name to make some determination of access to the User Profile store. I’m not sure why this is the case (not yet anyway).

Lesson learned (for now): make sure SharePoint’s default content access account can access the same domain URL on HTTP as that of the SPS3 protocol. As mentioned at the top of this post, I found this out on SharePoint 2016, and I need to test to see if the results are the same on SharePoint 2013.

[Update 5/13/2016]: Turns out I should read the TechNet articles carefully. The following article indicates using sps3s://mysite-url, which then works correctly.

https://technet.microsoft.com/en-us/library/hh582311.aspx?f=255&MSPPError=-2147217396

SharePoint 2016 MinRole Services List

Hopefully, by now we should all know about MinRole functionality in SharePoint 2016. If not, check out Bill Baer’s article here. In setting up my new farm, I was curious as to what services should live on what server to be in compliance. Of course, the point of MinRole is to save farm architects from worrying too much about this, but I was curious.

The list at the bottom of this post lists of all SP2016 services and the associated MinRole. I cannot take credit for the list, I got help from a post, located here, and converted the C# code to PowerShell:

    $servicesInRole = @{};
    $minRoleValues = [System.Enum]::GetNames([Microsoft.SharePoint.Administration.SPServerRole]);
    $minRoleValues | % { $servicesInRole.Add($_, (New-Object System.Collections.ArrayList)); }

    $farm.Services | % {
        $service = $_;
        $service.Instances | % {
            $serviceInstance = $_;
            # Check in which minrole the service can reside.
            $minRoleValues | % {
                if ($serviceInstance.ShouldProvision($_)) {
                    [System.Collections.ArrayList]$item = $servicesInRole.Get_Item($_);
                    if (!$item.Contains($service.TypeName)) {
                        $item.Add($service.TypeName) | Out-Null;
                    }
                }
            }
        }

    }

Interestingly, the list includes a role called “SingleServer”, which is not the same as “SingleServerFarm”. I’ve not yet tried adding a new server to my farm with this “SingleServer” role. I imagine this is for specific purpose, since it includes the insights service, but not much else.

I’ve been reading some comments about MinRoles and there appears some confusion surrounding the purpose of a MinRole. Essentially, a MinRole is a default configuration for a SharePoint server in the farm, based on the role the server will play in real life. For example, the “WebFrontEnd” role consists of services optimized for front end content delivery, since users typically hit these servers directly (via load balancer). The “Application” role consists of services optimized for back end processing.

Notice that some services exist in multiple MinRole configurations. For example, the “WebFrontEnd” and “Application” roles both contain the Business Connectivity Services. It’s feasible that both end users and back end processes require access to BCS. Therefore the BCS service lives in both role configurations, likewise with the Secure Store Service etc. This might upset some minimalist architects who like to deploy all services to one (or many) application servers and just web application services to WFE servers. If you think about it, it’s probably better to deploy some services to WFE servers when these services deliver content to end users.   If you’re looking for fine grained control over deployment location of SharePoint services, use the “Custom” role.

Something I found out after adding a new WFE server to my farm, which had services preconfigured on my App server – SharePoint started services included in the “WebFrontEnd” MinRole on my WFE server automaticlly. As I should have expected.

MinRole: WebFrontEnd

Service: Access Services 2010
Service: Microsoft Project Server Events Service
Service: Secure Store Service
Service: Microsoft SharePoint Foundation Web Application
Service: Request Management
Service: SSP Job Control Service
Service: Project Server Application Service
Service: PerformancePoint Service
Service: Visio Graphics Service
Service: Managed Metadata Web Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft SharePoint Foundation Sandboxed Code Service
Service: Microsoft Project Server Calculation Service
Service: Microsoft SharePoint Foundation Tracing
Service: SharePoint Server Search
Service: Microsoft SharePoint Foundation Timer
Service: App Management Service
Service: Security Token Service
Service: Machine Translation Service
Service: Microsoft Project Server Queuing Service
Service: Microsoft SharePoint Foundation Usage
Service: Microsoft SharePoint Foundation Subscription Settings Service
Service: Claims to Windows Token Service
Service: User Profile Service
Service: Business Data Connectivity Service
Service: Access Services
Service: Microsoft SharePoint Insights
Service: Information Management Policy Configuration Service

MinRole: SingleServerFarm

Service: Access Services 2010
Service: Microsoft Project Server Events Service
Service: Secure Store Service
Service: PowerPoint Conversion Service
Service: Microsoft SharePoint Foundation Web Application
Service: Request Management
Service: SSP Job Control Service
Service: Project Server Application Service
Service: PerformancePoint Service
Service: Visio Graphics Service
Service: Managed Metadata Web Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft SharePoint Foundation Sandboxed Code Service
Service: Microsoft Project Server Calculation Service
Service: Microsoft SharePoint Foundation Tracing
Service: SharePoint Server Search
Service: Microsoft SharePoint Foundation Timer
Service: App Management Service
Service: Security Token Service
Service: Machine Translation Service
Service: Microsoft Project Server Queuing Service
Service: Application Discovery and Load Balancer Service
Service: Microsoft SharePoint Foundation Usage
Service: Microsoft SharePoint Foundation Subscription Settings Service
Service: Search Administration Web Service
Service: Word Automation Services
Service: Claims to Windows Token Service
Service: User Profile Service
Service: Business Data Connectivity Service
Service: Lotus Notes Connector
Service: Microsoft SharePoint Foundation Workflow Timer Service
Service: Access Services
Service: Microsoft SharePoint Insights
Service: Search Host Controller Service
Service: Information Management Policy Configuration Service
Service: Microsoft SharePoint Foundation Incoming E-Mail
Service: Search Query and Site Settings Service

MinRole: SingleServer

Service: Microsoft SharePoint Foundation Database
Service: Security Token Service
Service: Microsoft SharePoint Insights

MinRole: Invalid

Service: Microsoft SharePoint Foundation Database

MinRole: Search

Service: SSP Job Control Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft SharePoint Foundation Tracing
Service: SharePoint Server Search
Service: Microsoft SharePoint Foundation Timer
Service: Security Token Service
Service: Application Discovery and Load Balancer Service
Service: Microsoft SharePoint Foundation Usage
Service: Search Administration Web Service
Service: Claims to Windows Token Service
Service: Microsoft SharePoint Insights
Service: Search Host Controller Service
Service: Search Query and Site Settings Service

MinRole: Application

Service: Microsoft Project Server Events Service
Service: Secure Store Service
Service: PowerPoint Conversion Service
Service: Microsoft SharePoint Foundation Web Application
Service: Request Management
Service: SSP Job Control Service
Service: Project Server Application Service
Service: Managed Metadata Web Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft Project Server Calculation Service
Service: Microsoft SharePoint Foundation Tracing
Service: Microsoft SharePoint Foundation Timer
Service: App Management Service
Service: Security Token Service
Service: Machine Translation Service
Service: Microsoft Project Server Queuing Service
Service: Application Discovery and Load Balancer Service
Service: Microsoft SharePoint Foundation Usage
Service: Microsoft SharePoint Foundation Subscription Settings Service
Service: Word Automation Services
Service: Claims to Windows Token Service
Service: User Profile Service
Service: Business Data Connectivity Service
Service: Microsoft SharePoint Foundation Workflow Timer Service
Service: Microsoft SharePoint Insights
Service: Information Management Policy Configuration Service
Service: Microsoft SharePoint Foundation Incoming E-Mail

MinRole: DistributedCache

Service: Microsoft SharePoint Foundation Web Application
Service: Request Management
Service: SSP Job Control Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft SharePoint Foundation Tracing
Service: Microsoft SharePoint Foundation Timer
Service: Security Token Service
Service: Microsoft SharePoint Foundation Usage
Service: Claims to Windows Token Service
Service: Microsoft SharePoint Insights

MinRole: Custom

Service: Microsoft SharePoint Foundation Web Application
Service: SSP Job Control Service
Service: Microsoft SharePoint Foundation Administration
Service: Microsoft SharePoint Foundation Database
Service: Portal Service
Service: Microsoft SharePoint Foundation Tracing
Service: Microsoft SharePoint Foundation Timer
Service: Security Token Service
Service: Microsoft SharePoint Foundation Usage
Service: Claims to Windows Token Service
Service: Microsoft SharePoint Insights

 

SharePoint PowerShell Scripts

It’s time to give my blog a fresh injection of content….

I’ve been working for several months on a lot of PowerShell script work for SharePoint (2010, 2013, and SharePoint Online). I figured it was about time that I put sanitized copies of my scripts up on my blog site for all to read.

I’ve added a new section on my blog, aptly named “Scripts”, which you can access via the top level navigation of this site. From there, I present an ongoing set of links to each script I develop and publish. At this time, the following is a list of the client-side scripts I’ve uploaded…

To access my SharePoint 2013 farm provisioning scripts, see here on GitHub.

Bulk Check In for SP2010 Files with No Version Info

SharePoint best practice is to disable “require check in” on document libraries before doing a large bulk import of documents. I received an email from a customer last week, who had not followed this best practice and had over 35,000 documents checked out, which no one but he could see.

Unlike checked out documents with previous check in version(s), a newly uploaded document is not visible by anyone but the person who uploaded the file, even administrators. Fortunately, SharePoint provides a way for an admin to take ownership of these documents via “Manage checked out documents” in the library settings. However, when dealing with a document count that exceeds the default threshold of 10,000, SharePoint returns an error. Temporarily increasing the threshold gets around the error, but then the user interface becomes intolerably slow.

Even after taking ownership, then there’s the task of bulk check in, which is again, a slow process via the UI for large item count document libraries. What I wanted was a PowerShell script to both take ownership of the documents and then check them in. Below is the server-side script I created….

Note: I had to use server-side and not client-side PowerShell because CSOM does not expose checked out files method. The script was tested on SharePoint 2010.

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue;

function BulkCheckIn {
param([Microsoft.SharePoint.SPFolder]$folder);
$folder.Files | ? { $_.CheckOutStatus -ine "None" } | % {
Write-Host $_.ServerRelativeUrl;
$_.CheckIn("Initial check in", [Microsoft.SharePoint.SPCheckinType]::MajorCheckIn);
}
$folder.SubFolders | % { BulkCheckIn -folder $_; }
}

$site = Get-SPSite http://mysitecollection/;
$web = $site.OpenWeb('subsite/subsite');
$lib = $web.Lists['Documents'];
$lib.CheckedOutFiles | % {
Write-Host "Processing $($_.Url)";
$_.TakeOverCheckOut();
}
BulkCheckIn -folder $lib.RootFolder;

Filter Document Lib to Last Published Version

My customer brought up an interesting requirement to filter their document library to show just the last approved versions when content approval and major/minor versions applied.

Any unpublished document – that is a document where the major version is 0 will not show up in the filter. Any document that has a major version number greater than 0 and is in draft or pending status – e.g a document at version 1.1 – will only show a link to the last published version.

Turns out the solution was quite easy and involved just adding some query string parameters…

?IncludeVersions=TRUE&FilterField1=_ModerationStatus&FilterValue1=0&FilterField2=_IsCurrentVersion&FilterValue2=1

The IncludeVersions parameter instructs the list view to show all versions. Then it’s a simple case of filtering on the most current version of each item where not in moderation state.

SharePoint Related Fields

I had an interest request from a client I was working with this week. They wanted
me to create a list for their store tracking business, which consisted of a large
number of columns. No big deal! That is until my client indicated that they wanted
to associate notes with each column for each list item entered into the list.


image

The image attached to this post shows the standard SharePoint New and Edit Form
for a list item. Ignore the fact that this is SharePoint 2007, because my solution
works just as well in SharePoint 2010 and 2013. Notice that each field has a notes
link, which when clicked will display a text box for adding additional notes, as
shown.

You may be thinking “just add additional columns of multiple lines of text to the
list”, and I considered this approach, but my client wanted the ability to add a
new column in their list without having to remember to add an additional column
for the notes. Further, they wanted a way to associate the notes with the column
automatically.

My next instinct was to use custom fields and custom field controls, which turned
out to be the core of my solution. Custom fields and field controls can be a pain
at times, and not always behave as predicted, but fortunately my client only used
single line of text, yes/no, lookup, choice, and date-time columns, so I was able
to derive custom versions of these controls to provide the behavior I was looking
to achieve.

The standard SPField type, from which all custom fields derive, contains a property
called “RelatedField”. This related field contains the title of a related field
to current column. The SPField class also includes some event handlers for the added
and deleting events, which I used to automatically create notes fields whenever
a new column is added to the list.

Let’s start with one of my custom SPField classes, which derives from the SPFieldText
to add custom logic to the stock single-line-of-text field type:

 
    public class CustomFieldText : SPFieldText
    {
        #region Fields

        private readonly SPFieldCollection _fields;

        #endregion Fields

        #region Construction

        public CustomFieldText(SPFieldCollection fields, string fieldName)
            : base(fields, fieldName)
        {
            _fields = fields;
        }

        public CustomFieldText(SPFieldCollection fields, string typeName, string displayName)
            : base(fields, typeName, displayName)
        {
            _fields = fields;
        }

        #endregion Construction

        #region Properties

        public override BaseFieldControl FieldRenderingControl
        {
            [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
            get
            {
                BaseFieldControl fieldControl = new CustomFieldTextControl();
                fieldControl.FieldName = InternalName;
                return fieldControl;
            }
        }

        #endregion Properties

        #region Methods

        public override void OnAdded(SPAddFieldOptions op)
        {
            CustomFieldHelper.CreateSlaveField(this);
        }

        public override void OnDeleting()
        {
            CustomFieldHelper.DeleteSlaveField(_fields, this);
        }

        #endregion Methods
    }

Looking at the previous class, there really isn’t much to my implementation. My
class implements the standard constructors for an SPField derived class, overrides
the FieldRenderingControl property because I wish to use my own, and overrides the
OnAdded and OnDeleting events, which enables me to detect when a column of my field
type is created or deleted. The interesting code logic exists in my helper class,
as follows:

 
    static class CustomFieldHelper
    {
        private const string RenderFieldSuffix = "_Shaddow";

        public static void CreateSlaveField(SPField master)
        {
            if (null == master) throw new ArgumentNullException("master");
            // Create a shadow field to store the value we want to display
            // in list views.
            var list = master.ParentList;
            if (null == list) return;
            // We only need a shadow copy when associated with a list.
            var relatedFieldName = master.InternalName + RenderFieldSuffix;
            var relatedDisplayName = String.Format("{0} Notes", master.Title);
            var sb = new StringBuilder();
            sb.Append("<Field Type="Note" ReadOnly="TRUE" ");
            sb.AppendFormat("Name="{0}" ", relatedFieldName);
            sb.AppendFormat("DisplayName="{0}" ", relatedFieldName);
            sb.Append("Sortable="TRUE" Filterable="TRUE" ");
            sb.Append("EnableLookup="FALSE" SourceID="http://schemas.microsoft.com/sharepoint/v3">");
            sb.AppendFormat("<FieldRefs><FieldRef Name="{0}" /></FieldRefs>", master.InternalName);
            sb.Append("<DisplayPattern><HTML><Column HTMLEncode="FALSE"/></HTML></DisplayPattern>");
            sb.Append("</Field>");
            list.Fields.AddFieldAsXml(sb.ToString());
            var field = list.Fields[relatedFieldName];
            field.Title = relatedDisplayName;
            field.RelatedField = master.Title;
            field.Update(true);
        }

        public static void DeleteSlaveField(SPFieldCollection fields, SPField master)
        {
            if (null == fields) throw new ArgumentNullException("fields");
            if (null == master) throw new ArgumentNullException("master");
            var relatedFieldInternalName = master.InternalName + RenderFieldSuffix;
            if (!fields.ContainsField(relatedFieldInternalName)) return;
            var field = fields.GetFieldByInternalName(relatedFieldInternalName);
            field.ReadOnlyField = false;
            field.Hidden = false;
            field.Update();
            fields.Delete(relatedFieldInternalName);
        }

        public static void SaveValueToSlave(SPListItem item, string value, SPField master, bool callUpdate)
        {
            if (null == item) throw new ArgumentNullException("item");
            if (null == master) throw new ArgumentNullException("master");
            if (null == value) return;
            var relatedFieldInternalName = master.InternalName + RenderFieldSuffix;
            var list = item.ParentList;
            var field = !list.Fields.ContainsField(relatedFieldInternalName) ? 
                list.Fields.Cast<SPField>().FirstOrDefault(f => f.RelatedField == master.Title) : 
                list.Fields.GetFieldByInternalName(relatedFieldInternalName);
            if (null == field) return;
            item[field.Id] = value;
            if (callUpdate) item.SystemUpdate();
        }

        public static string GetValueFromSlave(SPListItem item, SPField master)
        {
            if (null == item) throw new ArgumentNullException("item");
            if (null == master) throw new ArgumentNullException("master");
            var relatedFieldInternalName = master.InternalName + RenderFieldSuffix;
            var list = item.ParentList;
            var field = !list.Fields.ContainsField(relatedFieldInternalName) ?
                list.Fields.Cast<SPField>().FirstOrDefault(f => f.RelatedField == master.Title) :
                list.Fields.GetFieldByInternalName(relatedFieldInternalName);
            if (null == field) return "";
            var obj = item[field.Id];
            return null == obj ? "" : obj.ToString();
        }

        public static Control GetNotesMarkUp(out TextBox notesCtrl, SPField field)
        {
            if (null == field) throw new ArgumentNullException("field");
            var ph = new PlaceHolder();
            // Does shaddow field have data?
            var ident = (null != SPContext.Current.ListItem)
                            ? GetValueFromSlave(SPContext.Current.ListItem, field)
                            : null;
            var jsStr = String.Format("document.getElementById('{0}').style.display='block';", field.Id);
            ph.Controls.Add(!String.IsNullOrEmpty(ident)
                                ? new LiteralControl(String.Format(
                                    "<a href='#' onclick="{0}"><b>Notes</b> •</a>", jsStr))
                                : new LiteralControl(String.Format("<a href='#' onclick="{0}">Notes</a>", jsStr)));
            ph.Controls.Add(new LiteralControl(String.Format("<div id='{0}' style='display:none;'>", field.Id)));
            var table = new HtmlTable { Width = "100%" };
            ph.Controls.Add(table);
            ph.Controls.Add(new LiteralControl("</div>"));
            var row = new HtmlTableRow();
            var header = new HtmlTableCell();
            var headerText = new LiteralControl("<span class='ms-formlabel'><H3 class='ms-standardheader'>Enter notes below</H3></span>");
            header.Controls.Add(headerText);
            row.Controls.Add(header);
            table.Controls.Add(row);
            row = new HtmlTableRow();
            var cell = new HtmlTableCell();
            notesCtrl = new TextBox { TextMode = TextBoxMode.MultiLine, Rows = 6, Width = new Unit(100, UnitType.Percentage) };
            cell.Controls.Add(notesCtrl);
            row.Controls.Add(cell);
            table.Controls.Add(row);
            return ph;
        }
    }

In case you’re wondering why I didn’t just include my helper code in my custom field
class it’s because I created several custom field classes for my client and wanted
to reuse the same code. Since I’m inheriting from SharePoint’s classes I cannot
provide my own base class either, so a static helper class seemed like an easy approach.

The CreateSlaveField method is the most interesting of the previously show class.
This method creates a new Note field and adds it to the list that the master field
associates, it then populates the RelatedField property so I can find the association
later.

The notes field is only created if the master field is associated with a list. In
the case when a site owner creates a site column in a web, my method exits without
creating the slave field, because it’s only pertinent in lists. Since list columns
added from the web site columns gallery are new instances of the same field type,
the OnAdded method is called again when the new column adds to a list and this time
my method creates the slave column.

The DeleteSlaveField method removes the notes slave field when the master field
is removed from a list instance – this is just good house keeping.

When creating the slave field I set it as read only, this is so intrigued users
of the site cannot use the notes field for any other purpose than adding notes via
my custom field control, which brings me to my next class:

    public class CustomFieldTextControl : TextField
    {
        #region Fields

        private TextBox _notesCtrl;

        #endregion Fields

        #region Methods

        protected override void OnInit(EventArgs e)
        {
            CanCacheRenderedFieldValue = false;
            base.OnInit(e);
        }

        protected override void CreateChildControls()
        {
            if (IsFieldValueCached)
            {
                base.CreateChildControls();
                return;
            }
            if (null == Field) return;
            base.CreateChildControls();
            // Add the notes if in edit mode.
            if (ControlMode == SPControlMode.Edit || ControlMode == SPControlMode.New)
                base.Controls.Add(CustomFieldHelper.GetNotesMarkUp(out _notesCtrl, Field));
            // Update the controls with the current value stored.
            if (null != _notesCtrl && null != Field)
                _notesCtrl.Text = CustomFieldHelper.GetValueFromSlave(SPContext.Current.ListItem, Field);
        }

        public override void UpdateFieldValueInItem()
        {
            Page.Validate();
            if (!Page.IsValid) return;
            base.UpdateFieldValueInItem();
            // do actions after save
            if (null == Field) return;
            CustomFieldHelper.SaveValueToSlave(SPContext.Current.ListItem, _notesCtrl.Text.Trim(), Field, false);
        }

        #endregion Methods
    }

The previous and last class is my custom field control, which does the work of rendering
data from my custom field class. Again, this is a lightweight class, leaving the
heavy lifting to the field helper class.

The CreateChildControls method is called for any ASP.NET UI class (which custom
field controls ultimately derive) to load an child control instances. In this method,
I check that we’re not using a cached version of the control and that we’re in a
new or edit form, since my client didn’t want notes to appear in display only views.
I then inject HTML for the display of the notes text box, which is populated from
the contents of the associated slave control.

The overridden method UpdateFieldValueInItem ensures that the slave field in the
list item receives any text changes applied to the notes field when saving the list
item in the new/edit form.

That’s about it, except for the fldtypes_custom.xml file, which I deploy to the
%HIVE%TemplatesXML folder to register my custom field type(s):

 
<FieldTypes>
  <FieldType>
    <Field Name="CAMLRendering">TRUE</Field>
    <Field Name="TypeName">CustomFieldText</Field>
    <Field Name="TypeDisplayName">CustomFieldText</Field>
    <Field Name="TypeShortDescription">Custom Single Line of Text (with Notes).</Field>
    <Field Name="ParentType">Text</Field>
    <Field Name="UserCreatable">TRUE</Field>
    <Field Name="AllowBaseTypeRendering">TRUE</Field>
    <Field Name="FieldTypeClass">CustomSharePoint.CustomFieldText, CustomSharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cd2be6bc9c119b34</Field>
  </FieldType>
</FieldTypes>

SharePoint Authentication and Session Management

What is authentication?

1. A security measure designed to protect a communications system against acceptance of a fraudulent transmission or simulation by establishing the validity of a transmission, message, or originator.
2. A means of identifying individuals and verifying their eligibility to receive specific categories of information.

Authentication is essentially the process of validating a user is who they say they are, such that they can gain access to a system – in this context, the system is SharePoint. Authentication is not authorization, which is the process in determine if a known user is permitted access to certain data in the system, after successful authentication.

SharePoint, much like any content management system, relies on user authentication to provide user access to secured content. Pre-SharePoint 2010, SharePoint relied on NTLM, Kerberos, or basic (forms-based) authentication protocols (their discussion out of scope of this text). SharePoint 2010 introduced Claims-based-Authentication (CBA), also present in SharePoint 2013. CBA consists of authentication abstraction, using a Secure Token Service (STS), and identification of users with multiple attributes –claims – not just the traditional username and password pair.

A Secure Token Service implements open standards. A typical STS implementation communicates over HTTPS, and packages user identity information (claim data) via signed and encrypted XML – Secure Assertion Markup Language (SAML). Examples of STS implementations are the STS engine in SharePoint 2010/2013, ADFS, and third party applications build using the Windows Identity Framework.

SharePoint Session Management

A user session in SharePoint 2010/2013 is the time in which a user is logged into SharePoint without needing to re-authenticate. SharePoint, like most secure systems, implements limited lifespan sessions – i.e. users may authentication with a SharePoint system, but they’re not authenticated with the system indefinitely. The length of user sessions falls under the control of session management, configured for each SharePoint Web Application.

SharePoint handles session management differently, depending on the authentication method in play (Kerberos, NTLM, CBA, Forms, etc.). This article discusses how SharePoint works with Active Directory Federated Services (ADFS) – an STS – to maintain abstracted user authentication and user session lifetime. The following is a sequence diagram of the default authentication and session creation process in SharePoint 2010/2013 when using CBA with ADFS.

The following is a summary of the authentication process, shown in the sequence diagram.

  1. A user requests a page in SharePoint from their browser this might be the home page of the site.
  2. SharePoint captures the request and determines that no valid session exists, by the absence of the FEDAUTH cookie.
  3. SharePoint redirects the user to the internal STS – this is important because the internal STS handles all authentication requests for SharePoint and is the core of the CBA implementation in SharePoint 2010/2013.
  4. Since we have configured SharePoint to use ADFS as a trusted login provider, the internal STS redirects the user to the ADFS login page.
  5. ADFS acquires credentials and authenticates the user.
  6. ADFS creates a SAML token, containing the user’s claims, as encrypted and signed.
  7. ADFS posts the SAML token to the internal SharePoint STS.
  8. The Internal STS saves the SAML token in the SAML Token Cache.
  9. SharePoint creates the FEDAUTH cookie, which contains a reference to the SAML token in the cache.
  10. The Internal STS redirects the user back to SharePoint, and then back to the original requested page.

Session Lifetime

The lifetime of a SharePoint session, when using ADFS, is the topic of much confusion. Ultimately, SharePoint determines whether a user has a current session by the presence of the FEDAUTH cookie. The default behavior of SharePoint is to store this persistent cookie on the user’s disk, with fixed expiration date. Before sending a new FEDAUTH cookie back to the user’s browser, SharePoint calculates the expiration of the cookie with the following formula:

SAML Token Lifetime – Logon Token Cache Expiration Window

The above values are important since they govern the overall lifetime of the FEDAUTH cookie, and hence the session lifetime. The following table describes each value and its source:

Configuration Value Description
SAML Token Lifetime This value, in minutes, is provided by the token issuer – ADFS. In the case of ADFS, each Relying Party configuration (one for each instance of SharePoint farm) has this value as part of the configuration.By default, SharePoint sets the session lifetime the same as this SAML token lifetime.

You can change this value using PowerShell and the ADFS command: Set-ADFSRelyingPartyTrust.

E.g.

Add-PSSnapin Microsoft.ADFS.PowerShell

Set-AdfsRelyingPartyTrust –TargetName “Relying Party Name” –TokenLifeTime 10

Logon Token Cache Expiration Window This value, in minutes, is provided by SharePoint STS and governs how long the SAML token remains active in the cache, and therefore how long the associated user session remains alive. For example, if ADFS sets the SAML Token Lifetime to 10 minutes and this value is set in the STS as 2 minutes then the overall SharePoint session lifespan is 8 minutes.

E.g.

$ap = Get-SPSecurityTokenServiceConfig

$ap.LogonTokenCacheExpirationWindow = (New-TimeSpan -minutes 2)

$ap.Update();

IIsreset

Sliding Session

A siding session is one where the session expiration time changes as a user interacts with the system. By default, SharePoint 2010/2013 does not offer sliding sessions. Each new session expires on a fixed time, based on the aforementioned formula, earlier in this text.

Use of a sliding session does not mean that we must compromise security. Should a user become inactive, a sliding session will timeout just as the fixed session, the main difference that a user can extend a sliding session with continued use of the SharePoint system.

Creation of sliding session requires configuration of the Relying Party in ADFS and the SharePoint Logon Token Cache Expiration. The following PowerShell configures the Relying Party to 60 minutes, which is the absolute maximum time that a session remains active should the user become inactive:

Add-PSSnapin Microsoft.ADFS.PowerShell
Set-AdfsRelyingPartyTrust –TargetName “Relying Party Name” –TokenLifeTime 60

The following PowerShell sets the Logon Token Cache Expiration in SharePoint STS, which forces the sliding session lifetime to 20 minutes.

$ap = Get-SPSecurityTokenServiceConfig
$ap.LogonTokenCacheExpirationWindow = (New-TimeSpan -minutes 40)
$ap.Update();
IIsreset

The above settings are only part of the solution. On their own we have a fixed session duration of 20 minutes, determined by the earlier mentioned formula subtracting the logon token cache expiration from the RP token lifetime. To make sure the session renews with continued activity, we must refresh the session (and FEDAUTH cookie), which we can achieve with an HTTP module. The following code is an excerpt to refresh the session with each HTTP request.

Persistent verses Session Cookies

By default, SharePoint stores the authentication/session (FEDAUTH) cookie as a persistent cookie on disk. This allows the user to close and reopen their browser and access SharePoint without having to re-authenticate. This behavior is not always desirable.

Fortunately, we can ask SharePoint to use in-memory cookies (session cookies) for the authentication (FEDAUTH) cookie, as follows:

$sts = Get-SPSecurityTokenServiceConfig
$sts.UseSessionCookies = $true
$sts.Update()
iisreset

Interview Questions for SharePoint Developer Position

See how you do…

Q1. Name two SharePoint API objects you should define to open a site collection and sub web.

Q2. What is the role of the Shared Services provider in Microsoft Office SharePoint Server 2007?

Q3. How man content databases can a site collection span?

Q4. Can a content database contain multiple site collections?

Q5. What SharePoint capability allows navigation access to multiple site collections in a single site collection?

Q6. What is STSADM?

Q7. What is a metadata property in the search configuration?

Q8. What SharePoint capability allows for the use of multiple domain names for the same SharePoint site application?

Q9. What is a SharePoint Feature and how are they deployed?

Q10. Name all 4 feature scope levels

Q11. Name two development approaches to creating a WSP (SharePoint Deployment Package)

Q12. Should you dispose an instance of SPWeb obtained from the ALLWEBS collection of an SPSite object?

Q13. Should you dispose the ROOTWEB object of an SPSite object?

Q14. Should you dispose the site or web objects referenced from SPCONTEXT?

Q15. Your site crashes and SharePoint reports a standard error message after installing some custom code, how would you diagnose the issue?

Q16. How would you apply a common branding to all pages on your publishing sites?

Q17. What feature do page layouts belong?

Q18. How many direct parent content types may a child content type have?

Q19. Comment on why explicit permissions given to list items is bad practice

Q20. Is “Contributors” a SharePoint Group or Permission Level?

Q21. What is the role of the term store?

Q22. Comment how SP2010 no longer uses the SSP, and the new approach to service architecture

Q23. What is the sandbox?

Q24. What is Claims-Based-Authentication?

Q25. What Microsoft Office application allows design and implementation of workflows visually?