Tuesday, December 23, 2014

Don't embed data view web parts within wiki pages on a publishing site

The Problem

If you have created a publishing site in order to use the various features that are associated with that site template, you may see this error message when you are attempting to customize a new page created on the site within SharePoint Designer:


Content in the embedded form may be changed by the server to remove unsafe content  .Do you want to reload your page to see the results of the save?

If you have embedded various web parts, such as Data Form Web Parts on the page, and attempt to save the page within designer, you will see the above message.  Frustratingly, sometimes it allows your custom web parts to save and you can view them in SharePoint.  However, other times it will remove one or more of your web parts from the page upon save, and they are gone, for good.

The Solution

Instead of creating the default page type if you wish to embed web parts in a SharePoint publishing site page, open the site in designer and choose one of the Web Part Page types:


Even though SharePoint itself is still going to define it as a wiki page on your site pages screen, the markup that causes the unsafe content reminder is not included.  So you can save without worrying about loosing any markup on save.

That's it.  Save yourself some headaches and choose this page type right from the start.

Friday, December 19, 2014

Search vs Metadata groupings

The Issue

SharePoint, particularly in Enterprise level settings (10000+ users), can contain thousands and thousands of data items, including documents, list items, external items, content and digital media.  Finding the specific piece of information the end user requires can be challenging.  There are three things typically done to make it easier to locate the correct information.

  1. Site architecture.  Specific sites or areas for specific document types, usually related to department (payroll, human resources) or activity (specific projects, team events).
  2. Search.  The "search" field that appears at the top of most SharePoint pages, and list specific searches configured for large lists.
  3. Metadata Groupings.  The information is organized to make rapid location easier, ie, a list is grouped by assignee, so that a specific assignee's task is more rapid to locate.

The first two types of organization tend to be well understood.  It makes sense that users would look for payroll documents on the payroll departments website, or look for project related documents on a project site.  Search itself is also well understood, as it is included on almost every large public facing website, and the use of search engines such as google has made the end user comfortable with this process.

However, metadata groupings are used less, both because they are less understood by the end user, and because they require the intelligent analysis of the information being stored to come up with a good metadata scheme.

If we are able to explain to the end user the advantages of using a metadata based organization over the other two methods, then we are able to implement this improvement in production and see the efficiency improvements in the resulting system.

This blog post looks at a classic search scenario, and provides evidence as to why, in this case, a metadata grouping is the best answer.

Searching by Date Range

For this example, we are going to be looking at a payroll system that provides the end users pay stubs.  The paystubs are released to the end users on pay day, and additional paystubs may be added in correction or if an employee has more than one position, each requiring their own paystub.

Over time, a SharePoint list that simply lists paystubs is going to become cumbersome, once the list gets beyond a certain size locating the correct historical paystub can become more difficult and involve a lot of clicks.

The classic way to solve this issue is to come up with a date range search.  Here, you have an end user enter in a start date and end date, click "search", and see the results of the search.  But is this the most efficient way?

Let's take a look from the user clicks perspective:

  1. User clicks start date field calendar.
  2. User clicks "previous month" multiple times within calendar to get correct month (1+).
  3. User types in year.
  4. User clicks end date field calendar.
  5. User clicks "previous month" multiple times within calendar to get correct month (1+).
  6. User types in year.
  7. User clicks "Search". Results are displayed.

This is a minimum 7 click activity, more if they have to manipulate the calendar more than one month or year in either direction. 

And, if the results they were looking for are not included in the date range, then they must redo all of the clicking above in order to create another date range that hopefully does include the paystub they were looking for.  There has got to be a better way!  Fortunately, their is.

Searching by Meta Data

If, instead of simply providing raw list data organized in rows, one row for each paystub, as the example above does, we group the records by date we can enable much more rapid location of a specific or multiple records within a date range.

In this case, we can add two fields to the pay sub list, "Month" and "Year".  These are calculated fields, extracted from the pay stub date.

We can then group our records by these fields, first by year, then by month.  SharePoint will sort months alphabetically so if we wish it to be in month order, we will have to apply the fix listed in this blog post:

http://stevessharepointnuggets.blogspot.ca/2014/12/grouping-by-dates-months-in-month-order.html

This grouping results in the following output:

Now, let's take a look how many clicks it takes to locate a specific record:

  1. User clicks + sign by the year they are looking for.
  2. User clicks + sign by the month they are looking for.
The results are displayed.  In only two clicks the user was able to locate the same pay stub record that was located with 7+ clicks above.

And, in the case where the first two clicks did not locate the correct paystub, they are one click away from being able to view any previous or subsequent month in the same year, or two clicks away from a different year.  A vast improvement over the classic date range search approach.

Best Approach is a Hybrid

This post outlined the importance of metadata when used for grouping records, and how it can be more appropriate than a custom search in specific situations. 

If you are successfully able to combine all three ways of accessing data, through site architecture, standard and custom searches, and groupings using metadata, you will see much greater efficiency finding information within SharePoint, increasing adoption and satisfaction with the platform.


Wednesday, December 10, 2014

Grouping by Dates (months) in month order, not alphabetical, internal and external lists (business lists).

The Problem

Being able to "group" related data using the group by function in SharePoint is a useful way of displaying and organizing data for the end user.  Grouping by project, category, or other metadatavalues allows data organization that greatly enhances the end users ability to rapidly find data.

So, it stands to reason that we may wish to be able to group by dates, where you first group by year, and then by month to organize the data, as follows:


However, within SharePoint, if you generate your month field as follows (calculated field against a list):

Month: =TEXT(Modified,"mmmm")

Then SharePoints group by function will sort by alphabetical order rather than month order.  If you are grouping by month, April will always appear first in the list (or last, if you have reverse sort order).  This is usually not what the end user wants.

Internal List Solution

A solution for internal lists already exists, and is outlined nicely in this blog post:

http://blog.pathtosharepoint.com/2013/10/31/trick-or-treat-group-items-by-month/

External List Solution

I needed to expand this by applying it to an external list so that I could get the group by sort order indicated in the screen shot above.

The key to the solution is the inclusion of white space.  SharePoint sorts by whitespace, making elements with less white space appear after those with more.  Even better (in this case), it strips white space from the final page meaning the whitespace fix is invisible to the end user.

So, I needed to insert whitespace into my month field in order to have the group by function correctly sort by month.

As I am using an external content type, I am using sql to populate the data values.  I first created the month field in sql as follows:

LEFT(DATENAME(MONTH, Pay_Purchase.Date),3) as groupByMonth

This value is returned as an external content type field in my external content type, and can be used as a group by field.

As is, the sql above will result in months sorted alphabetically by SharePoint, we need to insert whitespace to get the correct sort order.  This results in the following sql:

SPACE(MONTH(Pay_Purchase.Date)) + LEFT(DATENAME(MONTH, Pay_Purchase.Date),3) as goupByMonth

Now, when I group by the month field it is in month order. 

I can modify the sql as follows to get reverse order (this is also possible within SharePoint Designer 'Sort and Group' options within the business list webpart):

SPACE(12 - MONTH(Pay_Purchase.Date)) + LEFT(DATENAME(MONTH, Pay_Purchase.Date),3) as goupByMonth

Whether you are attempting to group by a date field on an internal or external list, the whitespace fix works the same.  You can now group by month and have the correct sequence of values.

Thursday, December 4, 2014

Business List BLOB handling enhancements (multiple blobs, hiding no blob found links, alternate row colors) for external content types

Introduction

If you have worked with SharePoint business lists and blobs, using an approach similar to:

http://msdn.microsoft.com/en-us/library/office/ff634782(v=office.14).aspx

then you are familiar with creating an external content type with a blob field, updating the BDCM model to include handling for that blob, and how to import and use the external content type in a business list.  All good so far.

"Out of the box" the business list displays the items as standard list rows with the variable names used as row titles.  So some styling is usually necessary to make the list meet client demands.  And, out of the box, all blobs are displayed as links, even if there is no blob to resolve to in the database.  This is fine if every record you display always has an associated document blob, but if you have records where there is no document associated, the link is still displayed, and you get an ugly SharePoint error when you click the link.

So this post covers these four changes:
  • Change header field names and styling
  • Alternate Row colors
  • Handle multiple blobs for one record
  • Hide blob links when no associated document present
This post assumes you have a fully functional business list embedded and working within a SharePoint page.

Change Header Field Names and Styling

The first task is relatively simple (non-dynamic).  Changing header names simply consists of:

1. Locate the header row start tag: <table id="BdwpRows" border="0" width="100%" cellpadding="2" cellspacing="0">
2. Locate your first header field by tag: <th class="ms-vb" align="left">
3. Locate the tag that specifies field title: <xsl:with-param name="fieldtitle">
4. Replace the xsl in the field title with your header name.

The final result is this, changes bolded:

        <th class="ms-vb" align="left">
          <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
            <xsl:with-param name="fieldname">@Employee_Number</xsl:with-param>
            <xsl:with-param name="fieldtitle">
              Employee #
            </xsl:with-param>
            <xsl:with-param name="displayname">
              <xsl:value-of select="$ColName_0" />
            </xsl:with-param>
            <xsl:with-param name="fieldtype">text</xsl:with-param>
          </xsl:call-template>
        </th>
Styling changes are just as simple.  You can directly inject style into the header field tag, or reference an external class such as below (if you also reference the style sheet either within your page or the master page):

<th class="DarkGray" align="left">

Alternate Row Colors

On larger or longer lists, a lot of displayed data can be hard to read if the background color for each row remains white.  We may wish to alternate row colors in order to make reading the rows easier.  This is how this is accomplished.

1. Locate the tag that identifies the beginning of the row elements within the business list web part: <xsl:template name="dvt_1.rowview">
2. Locate the tag that identifies the beginning of one of the row columns:      
<td class="ms-vb">
        <xsl:attribute name="style">
3. My table rows by default are all gray.  I want them to alternate white and gray.  So, insert the following xml within the style tag defined above:
      <xsl:choose>
             <xsl:when test="position() mod 2 != 0">background-color:white</xsl:when>
             <xsl:otherwise />
      </xsl:choose>
This will override the gray rows and provide a white row for every second row.  Final xml looks like:

<xsl:attribute name="style">
    <xsl:choose>
        <xsl:when test="$dvt_1_form_selectkey = @*[name()=$ColumnKey]">color:blue</xsl:when>
        <xsl:otherwise />
    </xsl:choose>
    <xsl:choose>
        <xsl:when test="position() mod 2 != 0">background-color:white</xsl:when>
        <xsl:otherwise />
    </xsl:choose>

  </xsl:attribute>

Multiple Blobs in One Record

If you have already created a business list with one field for blob handling, you may wish to add another to associate two (or more) documents with the same record.  Fortunately, this change is very simple.

First, you will need to update your external content type to pull back both binary blob fields from the database, and create a filename for the blob when it is downloaded.

Once this is done, you will export your BDCM as specified in the link at the beginnning of this post, and insert your markup to handle the two blobs.

I created two external content types, one with each blob type to isolate the markup for each and test to ensure each was working first.  Once this was done, I could modify the second blob markup so that I could insert it into the BDCM model that also contained the first.

The first thing to handle is changing the method call for the second blob.

If you already have one blob up and running, you will be familiar with describing the blob handling method with this tag:

<Method IsStatic="false" Name="PDFReadStream">

If you have multiple blobs, then you need to rename the method to keep it unique.  I renamed the second blob handling method as follows:

 <Method IsStatic="false" Name="PDFReadStream2">

We also need to ensure the calling markup uses the correct method, so within the blob markup locate the tag:

<MethodInstance Type="StreamAccessor" ReturnParameterName="PDFRead" ReturnTypeDescriptorPath="PDFRead[0].EEDRReport" Default="true" Name="PDFReadStream" DefaultDisplayName="EEDRReport">

Update this tag to call your new method name:

<MethodInstance Type="StreamAccessor" ReturnParameterName="PDFRead" ReturnTypeDescriptorPath="PDFRead[0].EEDRReport" Default="true" Name="PDFReadStream2" DefaultDisplayName="EEDRReport">
        
Finally, we need to ensure that the filename for you blob document is correct.  If you leave it as it is now, both document types in your business list will have the same filename.  Probably not desired functionality.

If you look at the sql in the next section, you will see that one of the values returned is a dynamically generated filename (filename for blob).  We want this filename to be associated with this document.

Locate the tag that specifies filename within your streamaccessor method:

<Property Name="FileNameField" Type="System.String">FileName</Property>

Update this to use the new name specified in the sql below and populated through the external content type:

<Property Name="FileNameField" Type="System.String">EEDRFileName</Property>

That's it!  As long as you have working markup for both blob types, then these changes will allow you to handle two or more blobs per record.

Hide Blob links Where No Document is Present

The final section of this post is the most complex.  The issue here is that SharePoint generates the blob link whether it actually resolves to a blob or not, there is no handling to detect if a document is present before the link is displayed.  So we have to build that.

The first step is to update the read list stored procedure or view you are using to populate the external content type.  We need to return a value that indicates the presence of a document, in this example, I am using the COUNT function.

Within the stored procedure, I now include a row that returns the number of documents associated with a record.  Here is the sql (in bold) I used to accomplish this:

SELECT...EEDR.EEDRReport,  -- binary blob
EP.Employee_Number + '_' + LEFT(CONVERT(VARCHAR, EP.Pay_Date, 120),10) + '_EEDRReport.pdf' as EEDRFileName,  -- filename for blob
(select COUNT(EEDRReport)
from EEDR
where PK1 = EP.PK1
and PK2 = EP.PK2) as EEDRCount  -- blob count

Once this is completed, update your external content type within SharePoint Designer to include this field. You should be able to open the read list operation associated with your external content type and the above stored procedure, and see the variable "EEDRCount" listed there on the "return parameters configuration" page of the config wizard.  Put a checkbox beside your new field to include it in the updated external content type.  Click save.

Create a new page within SharePoint and associate your updated business list. Ensure you can see the row count variable being displayed.

Open the SharePoint page within SharePoint designer, edit mode.

Locate the blob display tags, started with:
 <xsl:variable name="downloadUrl" ...

Locate the tag that generates the blob link:

 <xsl:attribute name="onclick">

Just below this tag, replace the "click to download" text with the following:

<xsl:choose>
            <xsl:when test="@EEDRCount != 0">Click to download</xsl:when>
             <xsl:otherwise />
           </xsl:choose>
Save this change and view in SharePoint.  If the link does not resolve to a blob, it will not be displayed.

As a final step, you can remove both the header field and row field for the Count variable, as there is no need for end users to see this value.

Remove the header markup:

  <th class="DarkGray" align="left">
          <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
            <xsl:with-param name="fieldname">@EEDRCount</xsl:with-param>
            <xsl:with-param name="fieldtitle">
              Row Count
            </xsl:with-param>
            <xsl:with-param name="displayname">
              <xsl:value-of select="$ColName_9" />
            </xsl:with-param>
            <xsl:with-param name="fieldtype">number</xsl:with-param>
          </xsl:call-template>
        </th>

Remove the row markup:

<td class="ms-vb">
        <xsl:attribute name="style">
           <xsl:choose>
             <xsl:when test="position() mod 2 != 0">background-color:white</xsl:when>
             <xsl:otherwise />
           </xsl:choose>
        </xsl:attribute>
        <xsl:variable name="fieldValue">
           <xsl:call-template name="LFtoBR">
             <xsl:with-param name="input">
              <xsl:value-of select="@EEDRCount" />
            </xsl:with-param>
           </xsl:call-template>
        </xsl:variable>
        <xsl:copy-of select="$fieldValue" />
      </td>

Save your changes and view in SharePoint.

That's it!  These updates to the default business list created by SharePoint will enhance end user experience and avoid ugly SharePoint errors when a document is not found by the BCS.




Wednesday, November 19, 2014

Embed a Data Form Web Part (DFWP) in a SharePoint Web Part Page

Introduction


SharePoint Data Form Web Parts allow the manipulation of XSLT to provide specifically styled and dynamically altered html markup that can greatly enhance the appearance of your data view web parts, and enhance functionality. 
This post covers the inserting a new data form web part into a web part page to allow access to the xlst markup for look and feel customization.
 

Source List

First, I created a SharePoint  list with the following fields and values:
 

 

Next, I created a new default view that filters by current user, so the current user will only see their own record:


In this scenario, there is only one user per record, so when I filter by the current user, I am assured of one record (one row) being returned.  I also populated the list with one record as shown above to allow testing and rendering of the resulting data.
 

Embed the Data Form Web Part


Within SharePoint Designer create a new web part page:

Place your cursor between the <ZoneTemplage></ZoneTemplate> tags as follows:
<WebPartPages:WebPartZone runat="server" Title="loc:LeftColumn" ID="LeftColumn" FrameType="TitleBarOnly"><ZoneTemplate>CURSOR HERE</ZoneTemplate></WebPartPages:WebPartZone> </td>
Click the “Insert” tab on the top menu, and choose “Data View”.  In my case, the only choice was “Empty Data View”, so I went ahead and selected that.  This resulted in the following markup being inserted:
<ZoneTemplate>
<WebPartPages:DataFormWebPart runat="server" IsIncluded="True" AsyncRefresh="True" FrameType="None" NoDefaultStyle="TRUE" ViewFlag="8" Title="DataView 1" PageType="PAGE_NORMALVIEW" __markuptype="vsattributemarkup" partorder="2" __WebPartId="{B420DA3A-F18D-4A10-88F5-5B3D7C38910B}" id="g_b420da3a_f18d_4a10_88f5_5b3d7c38910b">
<DataSources></DataSources><datafields/><XSL></XSL></WebPartPages:DataFormWebPart>

</ZoneTemplate>
Place your cursor within this markup and choose “Data Source” from the insert menu.  Choose your list:
If not already clicked, click “Data Source Details” and you should see your list fields on the right of the screen:

I want the three values “Opted In”, “Enrolled”, and “Record Count” to be displayed within my DFWP.  Ensure your cursor is within the blank Data Form Web Part you just created.  Select the three columns by clicking them in the current data source.  I then choose “Insert Selected Fields as” from the drop down at the top of the current data source window and choose “Single item view” as I know there will only be one row per user.  If you will have multiple rows in your solution, select multiple item view.  This will result in new markup in your DFWP that displays these fields and controls other actions.  The markup that is specific to the fields in this example looks like:
 <xsl:template name="dvt_1.rowview">
<tr>
 <td>
  <table border="0" cellspacing="0" width="100%">
   <tr>
    <td width="25%" class="ms-vb">
     <b>Enrolled:</b>
    </td>
    <td width="75%" class="ms-vb">
     <xsl:value-of select="@Enrolled"/>
    </td>
   </tr>
   <tr>
    <td width="25%" class="ms-vb">
     <b>Record Count:</b>
    </td>
    <td width="75%" class="ms-vb">
     <xsl:value-of select="@Record_x0020_Count"/>
    </td>
   </tr>
   <tr>
    <td width="25%" class="ms-vb">
     <b>Opted In:</b>
    </td>
    <td width="75%" class="ms-vb">
     <xsl:value-of select="@Opted_x0020_In"/>
    </td>
   </tr>
Save your page, and look at this web page within SharePoint and you will see:

That’s it!  You can now start modifying this xsl markup using a combination of html and xsl (<xsl:value-of select="@Record_x0020_Count"/>) value processing to modify the look and feel of the page, see functions outlined here:
 



Thursday, November 13, 2014

Javascipt Redirect Fails within SharePoint web part (window.location.href, document.location.href not working)

The Problem

When you create a web part within SharePoint, whether its a Content Editor Web Part or a Data Form Web Part, you may wish to include a button that has a redirect action.  This redirect action could be static or dynamic, they key is that you wish to use something like:

function goToLink()
{
    if (possible code that determines which link to use...)
    {
        window.location.href = '/SitePages/MyPage.aspx';
    }
}

<button  class="dashboard-button" onclick="goToLink();">Go To Link </button>

This very simple example looks like it should work every time, and if it is done outside of SharePoint on its own web page, it does.

If however you embed this code within a SharePoint web part, it will sometimes  work.  Some users will be able to click the button and have it redirect the first time.  Some users will have to click the button multiple times, each time resulting in a page refresh, before the button works.  This type of intermittent issue can also be very challenging to troubleshoot.

The Solution

Fortunately, this one is a really simple solution.  Simply add the button type to your button markup:

<button type="button" class="dashboard-button" onclick="goToLink();">Go To Link</button>

By default, SharePoint sets the button type as "submit", which has the action of submitting the entire page, rather than just your javascript.  If you look at the source code behind a web page in SharePoint, you will typically see a line like this:
 
<form method="post" action="HomePage.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="aspnetForm">

If your button is not defined as a button type, then it submits the entire form, including running the onsubmit event shown above. This may result in SharePoint javascript conflicts with the window.location object, where the window.location object is overwritten locally by SharePoint before your javascript redirect fires, effectively refreshing the page instead of redirecting.  By setting the type to button, just the javascript you created fires, without the unwanted conflicts with other page events accessing the same object.






Wednesday, November 12, 2014

Correctly displaying Currency within an External List (using BCS)

The Problem

If you are attempting to display a currency field in a SharePoint external list, one that is defined as 'money' datatype in sql server, you will loose the ability to display cents for those records that do not have cent values.  So, for example, this will display correctly:

124.55

This will not:

124.00

The first is displayed as:

$124.55

The second:

$124

The second value drops the two 0's that follow the dollar amount.  Our clients are used to seeing currency with the cents included, even when they are 0, so we have to handle this specifically within the markup that makes up the business list.

The Solution

Open the page that has the external list embedded in SharePoint designer, edit mode.

Locate the markup that defines the row view fields (not the header fields, they are similar).  This markup is identified by this tag:

<xsl:template name="dvt_1.rowview">
Locate the markup that displays your currency fields, something like:

<td class="ms-vb">
        $<xsl:value-of select="@Gross_Pay" />
</td>

Update the select field to include the following formatting information:

$<xsl:value-of select="format-number(@Gross_Pay, '#,###,###.00')" />

Save your changes.  Update other currency fields if you have multiples.  This will display the currency in the correct format, the cents will show up if they have a value, if they are blank, they will be replaced with two 00's.

Friday, October 10, 2014

The High Cost of Deep SharePoint Customizations


The Challenge
SharePoint is a powerful and diverse enterprise content and collaboration management tool.  As such, it can quickly generate sites, supporting information, and dynamic lists to support various business processes.  However, the advantages of using a tool such as SharePoint can be negated when deep customizations are part of the business requirements for the SharePoint specific solution. 

Optimizing the usage of SharePoint within your organization is going to have to include the ability to determine which processes are SharePoint support appropriate, and which processes require other alternatives.  How do we determine that?  How do we determine, up front, whether the effort put into a SharePoint solution is the best usage of our time, or whether in fact a custom development tool, such as .net, or a custom site developer, such as WordPress, would be a better answer?
 
Out of the Box vs Custom Development
The line that divides low effort from high effort solutions in SharePoint is the amount of required functionality that can be gleaned “Out of the Box”.
“Out of the Box” means any SharePoint configuration that can be done with SharePoint itself, or with SharePoint Designer’s built in tools.  So, for example, you have a customer list.  “Out of the Box” we can create and populate that list, and provide that information in one of a number of SharePoint provided views.  In designer we can further modify views and sorting options for custom data extraction using the built in features on the office ribbon.

If however the functionality we require means changing markup, that is, the html/xml/xls components that make up the page, then this is now deep customization.  We are bypassing the built in abilities of designer and SharePoint itself by creating custom markup to meet our required functionality.

Out of the Box” as far as design is concerned is using built in master pages or themes, or even importing a custom master page and applying it.  As soon as you move into custom styles, custom master page markup, and dynamic elements such as Javascript or JQuery, you are once again deep into customization.

The more features, functionality and appearance required by the business process that can be covered by Out of the Box functionality, the less your effort to make those elements work is going to be.  Deeper customization greatly increases the amount of time needed to meet end user requirements.

The simplest way to determine if something is "Out of the Box" is this question, "Did you push a button to do it?".  If you pushed a button on SharePoint itself or within designer to configure, its out of the box.  If you had to make markup changes, it is deep customization.

The Difference, Quantified


I am in the fortunate position of being able to look back on 7 years of custom solution development to see in a very literal sense the difference in development time the two approaches can take.
I took a look at two system that were very similar in basic functionality.  These two systems both have:
1.       External Content Types
2.       External Lists
3.       Internal Lists
4.       Workflows
5.       Site pages

One of these systems, a vacation request system, was developed solely using out of the box components.  The end result has the default appearance of lists and items being used, and built in view abilities and permissions used to control the look and feel of the data exposed to the end user.
The total amount of customization time from start to completion for the vacation request system was 6 weeks.
The second system was, at its core, almost functionality identical.  External lists were used to pull information from an external system, internal lists were used for support, workflows and site pages are used to present information.
Except, the second system is very different than the first in two key ways:
1.       The system was designed using wireframes to finalize look and feel without SharePoint specialist consultation.

2.       The functionality of the system is highly constrained to the wireframes; there was no flexibility around controls and functional flow that allow tweaking of the design back to a more SharePoint centric default control flow.  The wireframes are to be replicated exactly.

This process is more typically associated with custom development.   How long does it take to develop a system in SharePoint this way?  How much extra time does customizing SharePoint to this level take?
The total amount of development time for this system was 28 weeks.  How could a system that is nearly equivalent in core functionality take 5 times as much time to develop as a similar system?  Let’s take a look at where the time was spent.


The “Out of the Box” System


The out of the box vacation request system described above uses no customized markup for functionality, and no customized look and feel.  So, the time spent on the system configuration is as follows, 100% of configuration time using SharePoint and SharePoint Designer to develop the system using out of the box components:

 



The Customized System


The customized system was instead configured from web based wireframes describing functionality in detail.  Configuration consists of working through the required functionality, accomplishing as much as possible using Out of the Box components, and then using custom css, Javascript, JQuery and xsl to further modify look and feel and functional flow.
The time spend on this system was spent in three areas:
1.       Out of the Box configuration
2.       Custom functionality supporting user access and application flow.
3.       Custom look and feel.
How much time was spent on each?  If we look at the pie chart below, we can see the amount of time being spent on Out of the Box configuration is only 30% (8 weeks) of overall time.  Custom functionality takes an additional 30% (another 8 weeks) and applying style based changes, ranging from custom data view web part xsl to deep master pages changes has taken an additional 40% of time (about 12 weeks). 


 
As you can see by this Chart, imposing design and functional restrictions onto a SharePoint system comes at a high cost.  In some cases, extending the amount of configuration time by 4 or 5 times over what a core system would have been to develop.  In this case, it would probably be more efficient to design the same system in .net and embed the application in SharePoint after the fact.

Achieving a Balance


As you can see from the above two examples, imposing functionality and look and feel changes on SharePoint that are not “Out of the Box” can have a significant impact on the configuration time of any SharePoint custom solution.
So, when working with clients, we have to be very aware as SharePoint specialists what SharePoint can and can’t do out of the box.  And be able to advise during the design phase on what amount of effort various components will take.
As SharePoint Specialists, we typically prefer using a process where the client describes the business process(es) they want supported, and we then align SharePoint tools with those requirements to the best of SharePoints ability.  This way, the client is not tied into a specific interface or look and feel.
However, in some cases, this is not the situation we are placed in.  We are instead provided with a client who is more used to a traditional development model, such as .net, or web tool, such as wordpress, and so therefor is used to providing detailed interface wireframes and having a reasonable expectation of delivery within a predictable amount of time.  In this case, we have to be sure that the client is made aware of the cost of these level of changes, and we even have to be prepared to say:
“SharePoint is not a custom application development tool.  Out of the box, I can create a system that will support your business process effectively and well.  However, we have to accept that there are certain limits to interface and functionality that are provided by the platform.  If we decide that the required functionality and look and feel are far enough outside of the “Out of the Box” sweet spot, then perhaps this should not be a SharePoint developed solution.”
Ultimately, we will only see the rapid solution development advantages provided by SharePoint if we can meet most of the client’s needs using the Out of the Box tools.  If clients are insistent on specific functionality and look and feel, then the best solution may be to steer them to custom solution development, the second best and considerably more time consuming solution is to attempt to implement it in SharePoint anyway, making it very clear to the client the significant additional cost this will entail.
SharePoint is not a custom application development tool.  If the out of the box components do not meet the majority of your needs on a SharePoint system, then it may not be the best way to achieve your required solution.
Knowing the difference between a couple of tweaks and major customization however is key in being able to correctly set client expectations on configuration time duration.  Any time the client defines specific look, feel, and functional flow, we are probably moving into the major customization area.
I would always advise my clients away from deep customizations within SharePoint to allow more rapid application development and focus on aligning what SharePoint offers with what they need to support their process.  And would also advise them that custom application development using a product such as .net may be better suited and faster in development for highly specialized systems.  If, knowing all of that, they are still prepared to accept the costs, then we move ahead with custom configuration.  At least here we can avoid surprises and sticker shock when they see the cost of the specialized system, and allow them to make an informed decision up front.