Category: Development

SharePoint or Application Development related posts

SharePoint 2010 Business Application Blueprints Now Available

My book, SharePoint 2010 Business Application Blueprints has been officially released and is now available both in print and as an electronic download.

 

What you will learn from this book

You will see how to build the following SharePoint projects:

  • An Effective Intranet Site for your organization that maximizes the site’s ability to aggregate content and is highly effective at communicating important messages
  • A Workflow Out of Office Solution that allows users to manage their out of office dates and automate task assignments to a delegated resource
  • A Company Forms Site with the definition of form content types and organizing the forms into a usable interface 
  • An Engaging Community Site including custom features that can be used to enhance collaboration and provide an information sharing system
  • A Site Request and Provisioning System to help governance and compliance activities
  • A Project Site Template to support project initiatives and track Issues, Tasks, and Contacts
  • A Project Management Main Site that can aggregate the key metrics and status information from the project management sites previously created
  • A Task Rollup solution that can aggregate tasks from the specified sites
  • A dynamic Site Directory solution that leverages Search

 

Approach

The hands-on example solutions in this book are based on fictitious business development briefs, and they illustrate practical ways of using SharePoint in various business scenarios.

A chapter is dedicated to each example SharePoint solution covering step-by-step instructions for building the SharePoint solutions, aided by the extensive use of screenshots.

 

While it is late to the party for this product cycle, I think there are some great examples that will serve developers for years to come. 

CASPUG–Session Wrap-up

Great turnout for the September meeting of the Charlotte Area SharePoint User Group.  Thanks to everyone that attended, hopefully the session was beneficial to the attendees.

Here is a copy of the slidedeck on Slide Share:  Developing Reusable Workflow Features

Here is a copy of the sourcecode used during the presentation:  SPBlueprints.Activities

User Profiles – Adding a Custom Tab and Page

The User Profile Pages within the MySites area of SharePoint 2010 is significantly more robust than in previous versions.  I really like the concept of making it a central/social hub and using it as a central place to hold and maintain content.  While I generally shy away from major customizations to the Personal sites (aka My Content), the My Site Host that stores the User Profile Pages and Activity Stream provides a great place to load centralized content and features.  It could be for integrating into other social applications your organization might use, or perhaps a place to hold executive Dashboards.  Since the My Site Host is shared between all users within that profile store it is easier to maintain than the individual Personal sites.

I have found myself in a number of conversations recently with people asking about how to customize and extend the User Profile Pages but I have not seen any good sources of information on how to approach it.  Today we will look at how you can define a custom ApplicationPage that uses the User Profile format and how to link to it from within the tabs present in the User Profiles section.

Managing User Profile Tabs

For those that are not aware, the horizontal tabbed navigation displayed in the User Profiles section is actually powered by the QuickLaunch menu for the MySite Host site.  You can add a new tab simply by adding a new Heading entry for the QuickLaunch.

Here is a screenshot of the tabbed QuickLaunch on the User Profile page.

Here is what the QuickLaunch configuration looks like:

 

Custom User Profile ApplicationPage

The sample code included in this project includes the standard formatting for the page so that it blends in perfectly with the other pages within the section.  Your custom page has access to the full Server OM and can include complex user controls or visualizations.

<%@ Page language="C#" DynamicMasterPageFile="~masterurl/custom.master" Inherits="Microsoft.SharePoint.Portal.WebControls.PersonalAdminPage,Microsoft.SharePoint.Portal,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="OSRVWC" Namespace="Microsoft.Office.Server.WebControls" Assembly="Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="OSRVUPWC" Namespace="Microsoft.Office.Server.WebControls" Assembly="Microsoft.Office.Server.UserProfiles, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SEARCHWC" Namespace="Microsoft.Office.Server.Search.WebControls" Assembly="Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderLeftNavBarDataSource" runat="server">
<SPSWC:DataSourceReplacer runat="server" ControlID="PlaceHolderLeftNavBar/QuickLaunchNavigationManager/QuickLaunchMenu">
<SPSWC:MySiteDataSource runat="server" ShowStartingNode="False" SiteMapProvider="MySiteLeftNavProvider" />
</SPSWC:DataSourceReplacer>
</asp:Content>

<asp:Content ID="Content2" contentplaceholderid="PlaceHolderPageTitle" runat="server">
Custom Page Title
</asp:Content>

<asp:Content ID="Content3" contentplaceholderid="PlaceHolderPageTitleInTitleArea" runat="server">
Custom Page Title
</asp:Content>

<asp:Content ID="Content4" contentplaceholderid="PlaceHolderSiteName" runat="server">
<SPSWC:PersonalSpaceMainHeading TitleMode="true" runat="server" />
</asp:Content>

<asp:Content ID="Content5" contentplaceholderid="PlaceHolderMain" runat="server">
<Sharepoint:FormDigest runat="server" id="FormDigest" />

<div class="s4-notdlg noindex">
<a id="A1" href="javascript:;" onclick="javascript:this.href='#SkipContactCard';" class="ms-SkiptoMainContent" runat="server">
<SPSWC:StringValueEx ResourceFile="spscore" LocId="SkipContactCard_Text" runat="server" />
</a>
</div>
<SPSWC:ProfilePropertyLoader id="m_objLoader" LoadFullProfileOfCurrentUser="true" runat="server" />
<div class="ms-profilepageheader">
<table border="0" cellpadding="3" cellspacing="0" ID="ZoneTable" width="100%" class="ms-tztable">
<!-- business card zone -->
<tr><td colspan=3 width="100%" style="padding:24px">
<table class="ms-contactcardtext3" cellspacing=0 cellpadding=0 width="100%">
<tr>
<td width="150" valign="top">
<div class="ms-contactcardtext3" style=""width:100%;">
<SPSWC:LocalTimeControl runat="server" />
</div>
<div class="ms-profilepicture ms-contactcardpicture ms-largethumbnailimage">
<SPSWC:ProfilePropertyImage PropertyName="PictureUrl" RenderWrapTable="False" ShowPlaceholder="true" id="PictureUrlImage" ImageSize="Large" CenterVertically="true" runat="server"/>
</div>
<div class="ms-contactcardtext3" style="width:100%;">
<div><SPSWC:EditProfileButton runat="server" id="ddlEditProfile" /></div>
<div><SPSWC:AddToColleaguesButton runat="server" id="TBBAddtoColleagues" class="ms-my-addcolleague"/></div>
</div>
</td>
<td valign="top" width="320">
<div style="min-height:175px">
<div class="ms-contactcardtext2 ms-identitypiecenotediv">
<SPSWC:StatusNotesControl runat="server"/>
</div>
<div style="padding-top: 50px;vertical-align:top;" class="ms-name ms-contactcardtext1" id="ProfileViewer_Name">
<SPSWC:PersonalSpaceMainHeading TitleMode="false" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueTitle">
<SPSWC:ProfilePropertyValue PropertyName="Title" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueDept">
<SPSWC:ProfilePropertyValue PropertyName="Department" runat="server"/>
</div>
<br/>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueOfficePhone">
<SPSWC:ProfilePropertyValue PropertyName="WorkPhone" dir="ltr" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueOfficeLocation">
<SPSWC:ProfilePropertyValue PropertyName="Office" runat="server"/>
<SPSWC:ProfilePropertyCheckValue PropertyNames="Office,Location" runat="server" Text=", "/>
<SPSWC:ProfilePropertyValue PropertyName="SPS-Location" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueEmail">
<SPSWC:ProfilePropertyValue PropertyName="WorkEmail" runat="server" PrefixBrIfNotEmpty="true"/>
</div>
<br/>
<div style="padding-left: 20px;display:none;" class="ms-contactcardtext3" id="ProfileViewer_ProfileDetails" >
<SPSWC:ProfileDetailsViewer id="ProfileViewer" Collapsible="false" runat="server" />
</div>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3">
<SPSWC:ContactCardDetailsLink AboutMeId="ProfileViewer_ValueAboutMe" ProfileDetailsId="ProfileViewer_ProfileDetails" EllipsisId="ProfileViewer_Ellipsis" runat="server" />
</div>
</td>
<td valign="top" width="418">
<div style="padding: 50px 5px 0px 15px; overflow:hidden; min-height: 142px; height: 142px;" class="ms-contactcardtext3" id="ProfileViewer_ValueAboutMe">
<SPSWC:ProfilePropertyValue PropertyName="AboutMe" runat="server" PrefixBrIfNotEmpty="false"/>
</div>
<div style="padding-left: 15px;display:none" id="ProfileViewer_Ellipsis" class="ms-contactcardtext3"></div>
</td>
</tr>
</table>
</td></tr>
</table>

<SharePoint:AspMenu
ID="MySiteSubNavigationMenu"
Runat="server"
EnableViewState="false"
DataSourceID="MySiteSubNavDS"
AccessKey="<%$Resources:wss,navigation_accesskey%>"
UseSimpleRendering="true"
UseSeparateCss="false"
Orientation="Horizontal"
StaticDisplayLevels="1"
MaximumDynamicDisplayLevels="0"
PopOutImageUrl=""
SkipLinkText=""
CssClass="s4-sn">
</SharePoint:AspMenu>
<SPSWC:MySiteDataSource
ShowStartingNode="False"
SiteMapProvider="MySiteSubNavProvider"
id="MySiteSubNavDS"
runat="server"/>

</div>

<a name="SkipContactCard" tabindex=0></a>

<div class="ms-profilepagecontent">
Your Page Content Here!
</div>
</asp:Content>

Managing the QuickLaunch Entries

For this to be a robust feature the QuickLaunch entries should be managed as part of the feature activation/deactivation process so a FeatureReceiver has been added to the project to add and remove the entries respectively.

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Security;

namespace CustomMySitePage.Features.Customer_User_Profile_Page
{
[Guid("d2754488-4038-42fd-9c9e-f93f19d4e093")]
public class Customer_User_Profile_PageEventReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
SPNavigationNode node = new SPNavigationNode("Custom Page", web.Url + "/_layouts/CustomMySitePage/PageTemplate.aspx");
web.Navigation.QuickLaunch.AddAsLast(node);
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
SPNavigationNodeCollection nodeCol = web.Navigation.QuickLaunch;
foreach (SPNavigationNode node in nodeCol)
{
if (node.Title == "Custom Page")
{
node.Delete();
}
}
}
}
}
}

Deployed Custom Tab and Page

Here is a final look at what it looks like with the feature activated and the Tab and Page available.

 

Template Project Source Code

The source code for this project is available for download here:  CustomMySitePage Project

I will not be providing support for the code as it is merely an example of how the pages within the User Profile section of MySites works.

Use the Status Bar to Display Active Workflows

Over the past six months I’ve found myself moving more and more towards using the new Client OM for things that I used to use jQuery and SPServices for.  While I still love SPServices, it is not always required and now that I am getting more knowledgeable about how to do things in the Client OM, things are getting a lot easier (although you have to go back to writing CAML).

After a recent session at SharePoint Saturday The Conference an attendee asked me about ways to show a user they have an open workflow task.  After thinking about it, it was pretty similar to something else I had done recently that involved reading for a SharePoint list and adding an item to the status bar using the Client OM.  This particular request involved a little more code in order to properly filter down the task list, but it works like a charm.  It is important to remember that the client OM only works within the given Site Collection, and this particular code as-is will only look at the site the user is on at the time.

If you want this to display on a single page, you can simply add it to the page. In order to have it show up throughout the site though, you will want to add it to the MasterPage. If the MasterPage is used throughout many sites and or site collections, it will show any open tasks for the specific site.

To start off, we will create a div container. I typically put something like this just inside the tag of the MasterPage. Add in some global variables and then add in the Ajax Execute/Delay command that will call the first method, getUser() when the page is fully loaded.

   <div id="SetStatus"><script type="text/ecmascript" language="ecmascript">
    var url = '';
    var statusId = '';
    var isitDlg = window.location.href.match(/isDlg/i) != null;
    var taskOutput;
    var context = null;
    var web = null;
    var curUser = null;

    if (!isitDlg) {
        ExecuteOrDelayUntilScriptLoaded(getUser, "sp.js");
    }
</script></div>[/sourcecode]   

Next, within the script block we will add the methods to support getUser(). These methods will get the current user's information which is needed to be able to load only the current user's assigned tasks.

[sourcecode language="js"] function getUser() { context = new SP.ClientContext.get_current(); web = context.get_web(); curUser = web.get_currentUser(); curUser.retrieve(); context.load(web); context.executeQueryAsync(Function.createDelegate(this, this.onSuccessMethod), Function.createDelegate(this, this.onFailureMethod)); } function onSuccessMethod(sender, args) { var user = web.get_currentUser(); LoadNotifications(); } function onFailureMethod(sender, args) { alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace()); }

With the user’s name available we can now read the list items from the local Task List that are active and assigned to the user. If successfull the (ReadListItemSucceeded() method is called otherwise if an error is raised the ReadListItemFailed() is called.

function LoadNotifications() {  	
var listTitle = "Tasks";  	
context = SP.ClientContext.get_current();  	
var list = context.get_web().get_lists().getByTitle(listTitle);  	
var camlQuery = new SP.CamlQuery();  	
camlQuery.set_viewXml("<view><query><viewfields><fieldref name="ID" /><fieldref name="Title" /><fieldref name="FileDirRef" /><fieldref name="StartDate" /><fieldref name="DueDate" /></viewfields><where><and><eq><fieldref name="AssignedTo" /><value type="User">" + curUser.get_title() + "</value></eq><or><eq><fieldref name="Status" /><value type="Choice">In Progress</value></eq><eq><fieldref name="Status" /><value type="Choice">Not Started</value></eq></or></and></where></query></view>");  	
this.listItems = list.getItems(camlQuery);  	
context.load(listItems);  	
context.executeQueryAsync(ReadListItemSucceeded, ReadListItemFailed);  
}  

The returned list of tasks is now processed and the output is prepared. If tasks are prepared, the SetStatus() method is called to show the status bar message.

function ReadListItemSucceeded(sender, args) {  	
var itemCount = 0;  	
taskOutput = " <div id="tasks"> <table width="800"> <tbody> <tr style="font-weight: bold"> <td width="500">Task</td> <td width="150">Start Date</td> <td width="150">Due Date</td></tr>";  	var items = listItems.getEnumerator();          
while (items.moveNext()) {             
itemCount += 1;             
var listItem = items.get_current();             
var isLate = false;             
taskOutput += "<tr><td><a href="&quot; + listItem.get_item(" FileDirRef') + "/DispForm.aspx?ID=" + listItem.get_item('ID') + "' target='_top'>" + listItem.get_item('Title') + "</a></td><td>" + listItem.get_item('StartDate') + "</td><td>" + listItem.get_item('DueDate') + "</td></tr>";         
}         
taskOutput += "</table></div>";         
if (itemCount > 0) {             
SetStatus("Reminder: ", "You have a workflow task! <a href="javascript:ShowTasks()">View open Workflow Tasks</a>", isLate);         
} 
} 
function ReadListItemFailed(sender, args) {         
alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace()); 
} 

The SetStatus() method shows how simple it is to work with the Client OM. The SP.UI.Status.addStatus call will post the message including the title and the body of the message. We then make a second call to SP.UI.Status.setStatusPriColor to change the color of the bar. Pretty simple, yet so powerfull!

function SetStatus(title, message, isLate) { 	
statusId = SP.UI.Status.addStatus(title, message, false);         
if (isLate) {             
SP.UI.Status.setStatusPriColor(statusId, 'red');         
}         
else {             
SP.UI.Status.setStatusPriColor(statusId, 'yellow');         }     } 

The last method is the ShowTasks method which is the javascript function that is called from within the status messages to show a simple output inside of the Client OM’s ModalDialog. First format the display options for the window and then call the SP.UI.ModalDialog.showModalDialot() method.

function ShowTasks() {  	
var _html = document.createElement('div');  	
_html.innerHTML = taskOutput;  	
var options = { html: _html, autoSize:true, allowMaximize:true, title: 'Open Workflow Tasks', showClose: true };  	
var dialog = SP.UI.ModalDialog.showModalDialog(options);  
}  

Not a lot of code to provide a very usefull solution that does not require anything to be deployed to the server. Below are screenshots of the solution in action.

Open Workflow Tasks Status

Open Workflow Tasks Listing

There are definitely some things that can be done to better format the output of the ModalDialog, but you get the idea. This is just one way to leverage the Client OM for some very useful business features.

Bulk Updates of User Profile Properties

This past week fellow SharePoint MVP Yaroslav Pentsarskyy posted an excellent PowerShell script for doing bulk updates on the UserProfile properties via PowerShell.  The Bulk Update SharePoint 2010 User Profile Properties is a great script that makes it extremely easy to populate any new fields that are not set to synchronize. 

My team has been doing a lot of client work promoting the user of User Profiles for use within customizations or to drive business processes.  For a quick overview check out my blog post Permanent Link to User Profiles – Driving Business Process or sit in on my Developing Reusable Workflow Features presentation at SharePoint Saturday NY on July 30th or SharePoint Saturday The Conference 2011 August 11-13th.

This also demonstrates another great example of the value that PowerShell can bring to Building and Maintaining a high functioning SharePoint environment.

Review – Pro SharePoint 2010 Branding and User Interface Design

I was recently given a chance to review this book thanks to one of the books author’s John Ross.  While my main focus is not on branding and user interfaces it is a critical component of the SharePoint solutions my team delivers and also one of the important things that can separate successful from failed implementations.

This book does a great job of providing an overview of the branding options with SharePoint 2010 (Foundation and Server).  It also does a really good job of showing how things have changed which is an important thing to understand since the there are so many new controls and features (ribbon) and the previous use of nested tables has been all but abolished. 

There are some valuable tips for “simple branding” which would is great information for Site Owners to have as they manage their local department or project site.  There is also great information on more advanced branding that can cover your overall marketing and communication standards.

One of the really nice things about this book is that it highlights some of the landmines that can pop up during your project.  Understanding where in an inheritance chain to style or not to remove the !Important commented items can save you hours of grief.  The Content Placeholder reference for MasterPages is also incredibly valuable.

From my perspective one of the most important chapters is on deployment.  Since user experience guys are not always coders they are not always familiar with how to properly deploy their brilliant work.  SharePoint Designer is a valuable tool, but it should not be used to maintain the branding and user interface changes for your environment (with very few exceptions).

All told this book provides a great overview of all of the areas and depth to important topics designers need to know.  This book should be a good fit for SharePoint Developers or Administrators needing to do simple branding, Site Owners looking to apply some customizations, or for Designers looking to create robust user interfaces on the SharePoint 2010 platform.

Professional SharePoint 2010 Branding and User Interface Design

Shared Link: SharePoint 2010 Social Networking Diagram

I do not often link to another blog post as part of one on my site, but this one was too good to pass up.  For those of you interested in SharePoint 2010’s Social Features, here is  great diagram of of how all of the components and services fit together.  It is extremely valuable to understand this information when you embark on an implementation or need to troubleshoot why something is not behaving as expected.

SharePoint Solutions Team Blog

SharePoint 2010 Social Networking Diagram

%d bloggers like this: