Welcome to my blog on all things SharePoint. I have a range of articles that will interest you if you've made it as far as visiting my blog. I was awarded as an SharePoint MVP by Microsoft in July 2010. I currently live in New York and am an Enterprise Architect at AvePoint Inc.. I co founded www.NothingButSharePoint.com with Mark Miller in 2010.

MVP AwardJeremy Thake Profile Photo

Whitepapers

NBSP

Check out my articles on NothingButSharePoint.com

Solution Development in SharePoint 2007

This series was inspired by the chatter amongst SharePoint blogs on the best ways to approach customisations in SharePoint using Solutions.

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

Leveraging the SharePoint Platform

This series was inspired by a discussion had with Andrew Coates at a Perth SharePoint User Group meeting. This then turned into a 6 part series on Arno Nell's SharePointMagazine.net web site.

Initial post - Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6

Webcasts

I have recorded various web casts that I present at User Groups or just on a specific topic by request:
How ASP.NET Developers can leverage SharePoint webcast
SPSource Webcast: Reverse engineer Lists to ListTemplates and much more
SharePoint Development with Unit Testing webcast
Perth SharePoint UG Web Cast on approaches to deploying artefacts (SPSource)
More...


Podcasts

I have been interviewed about Leveraging the SharePoint Platform by the SharePoint Pod Show: listen here .

RSS Feed Feed your read!

Archives

November 2012 (6)
October 2012 (8)
September 2012 (4)
August 2012 (7)
July 2012 (13)
June 2012 (4)
March 2012 (1)
February 2012 (1)
January 2012 (5)
September 2011 (2)
August 2011 (1)
July 2011 (3)
June 2011 (7)
May 2011 (3)
April 2011 (3)
March 2011 (3)
February 2011 (2)
January 2011 (1)
December 2010 (4)
September 2010 (4)
July 2010 (5)
June 2010 (4)
May 2010 (6)
April 2010 (7)
March 2010 (5)
February 2010 (7)
January 2010 (3)
December 2009 (1)
November 2009 (6)
October 2009 (9)
September 2009 (7)
August 2009 (6)
July 2009 (13)
June 2009 (4)
May 2009 (12)
April 2009 (4)
March 2009 (4)
February 2009 (13)
January 2009 (4)
December 2008 (4)
November 2008 (11)
October 2008 (16)
September 2008 (4)
August 2008 (5)
July 2008 (4)
June 2008 (8)
May 2008 (5)
April 2008 (9)
March 2008 (5)
February 2008 (6)
January 2008 (1)
November 2007 (11)
October 2007 (8)
September 2007 (24)
August 2007 (5)
July 2007 (2)
May 2007 (1)
April 2007 (1)
March 2007 (1)
February 2007 (3)
January 2007 (4)
November 2006 (7)
October 2006 (7)
September 2006 (18)
August 2006 (14)
June 2006 (3)
May 2006 (8)
April 2006 (4)
March 2006 (38)
February 2006 (30)
January 2006 (2)
December 2005 (3)
November 2005 (28)
May 2005 (1)
April 2005 (5)
March 2005 (1)
November 2004 (1)
August 2004 (11)
July 2004 (1)
Failed to render control: An error occurred during a call to extension function 'createMonthUrl'. See InnerException for a complete description of the error.

Links

Tag Cloud

Ajax, Apple, DotNetNuke, Enterprise Content Management, Error Resolution, Gadgets, General, Governance, Microsoft .Net Development, Mobile, SharePoint, Sharepoint Business Forms, Sharepoint Business Intelligence, Sharepoint Collaboration, SharePoint Development, Sharepoint Enterprise Content Management, Sharepoint Enterprise Search, Sharepoint Portal, US Migration, Web 2.0, Workflow
Jun 112008

Approved Page Layouts not showing in PublishingWeb

Had a "great" morning this morning involving releasing to our SharePoint 2007 Production environment which has been in place for nearly 18 months now - 6 server farm. Our UAT environment was built by myself last month which "matches" Production as closely as possible in terms servers etc. but it's been hard because the documentation of how Production built is incomplete. So far anyone out there who has built an environment, I would strongly recommend documenting ANY configuration change made to that Farm environment.

Anyway, I released to our Shared Dev environment fine and dandy and also to our UAT environment with no problems. Then I came across an error in Production, fortunately I had enough exception handling in there to throw and error when I called GetAvailablePageLayouts in the EventReceiver that was trying to create the page and didn't get back the Page Layout I expected in this collection!

$pubweb.GetAvailablePageLayouts() | ft Name, Title

Name Title

---- -----

ArticleLinks.aspx Article page with summary links

RedirectPageLayout.aspx Redirect Page

DefaultCPMiningLayout.aspx Default CP Mining Page Layout

Much digging around the web pointed me towards looking at SPWeb.AllProperties["__PageLayouts"] which in a default environment isn't even there but can also be string.Empty. In my Production environment it included a bunch of XML.

PS C:\Deployment> $web.AllProperties["__PageLayouts"]

<pagelayouts><layout guid="794ff91c-4d30-4d17-a342-bb39218a52a9" url="_catalogs/masterpage/ArticleLinks.aspx" /><layout

guid="bbcc0e4b-8b9f-45af-8383-9c2b63342e41" url="_catalogs/masterpage/RedirectPageLayout.aspx" /><layout guid="ff8de27

d-5ff6-4908-bf8c-520fc82bfc80" url="_catalogs/masterpage/DefaultCPMiningLayout.aspx" /></pagelayouts>

So, then started looking around at how this is set on the web and came across SetAvailablePageLayouts. But reading around it wasn't obvious how you got the PageLayout objects to put in the PageLayouts[] array parameter of the method. And Google said nothing on this :-(

I then dug some more and found this property on PublishingWeb called AllowAllPageLayouts , so I set that...and nothing seemed to happen! Then I noticed there is a Update() method also! So the key here is to call both in order ;-)

$baseURL = "http://intranet"
$web = global:Get-SPWeb($baseURL, $null)
$pubweb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)
$pubweb.AllowAllPageLayouts($true)
$pubweb.Update()
$pubweb.GetAvailablePageLayouts() | ft Name, Title

And here were the results! Phew!

PS C:\Deployment> $pubweb.GetAvailablePageLayouts() | ft NAme,Title

Name Title

---- -----

PageFromDocLayout.aspx Article page with body only

ArticleLeft.aspx Article page with image on left

ArticleRight.aspx Article page with image on right

ArticleLinks.aspx Article page with summary links

RedirectPageLayout.aspx Redirect Page

BlankWebPartPage.aspx Blank Web Part Page

DefaultCPMiningLayout.aspx Default CP Mining Page Layout

DocumentWebPartPage.aspx Document Web Part Page

WelcomeLinks.aspx Welcome page with summary links

WelcomeTOC.aspx Welcome page with table of contents

WelcomeSplash.aspx Welcome splash page

There was no documentation on this being locked down at the Site Collection RootWeb, these kind of changes can cause all sorts of issues when you're not expecting them! So again, I recommend documenting anything that is different from a base install when it comes to configuration, or at least have a Solution package that can configure it in a new environment.

I guess the big point is how far do you go with documenting and scripting configuration? The whole point of SharePoint is to be collaborative and let it evolve naturally. Realistically not every dev environment is going to have enough room to restore a entire Site Collection into it, so the rule of thumb is everything but the content should be re-deployable in your Dev / UAT environment. Where the line is drawn between Configuration and Content is left to discuss another day...

Published: 6/11/2008  8:43 PM | 2  Comments | 0  Links to this post

Jun 092008

Solution Development for SharePoint 2007 Part 8

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

OK, so there's plenty of hype at the moment for ASP.NET developers to "leverage the platform"...being SharePoint and a really flash (I mean silverlight) interface for them to click around on with the title "Do Less. Get More. Develop on SharePoint". This made me cringe after the last couple of weeks I've had on my current project and also with respect to these posts I've been putting up.

The site tries to push ten key aspects of SharePoint you get OOTB. Of which I have covered: Web Parts, Data Lists, Event Handlers and Custom Content Types already. I'll stress that I've only really scratched the surface on what these things can do, what I've shown you so far are the basics.

Now SharePoint isn't alone in this space...SharePoint sits on top of the .NET Framework, IIS and SQL much like other products out there. Some of which based on templates make it easy to generate database schemas, CRUD stored procedures, .NET DALs, .NET web services, ASP.NET presentation layers with a few clicks such as CodeSmith, MyGeneration, etc. Now granted you don't get User Management, Workflow engine...but you certainly can get Navigation and Page Branding with ASP.NET (although it has been extended with SharePoint).

Underpinning all of the ten really is the SharePoint Lists (SPList) at the core of:

  1. Pages for navigation - yes pages are stored in a list
  2. Web Parts - web parts are stored in a gallery that is a list
  3. Event Handlers - not all event handlers hang off lists, but a lot of the reasons for using them would be
  4. Workflow - see event handlers
  5. Custom Content Types - the underlying List schemas

As an example of this OOTB platform I decided to use my new found love of Powershell to generate some sample data in my Schedule list I created in previous posts.

$weburl = "http://jt-asuslaptop/TestTVShowSchedule";
$web = global:Get-SPWeb($weburl, $null);
$list = $web.Lists["Schedule"];
$shows = "Heroes", "Underbelly", "Battlestar Gallactica", "Prison Break", "Lost", "House", "Greys Anatomy", "24", "Desperate Housewives" #one for the girls
 
for($s=0; $s -lt $shows.Count; $s++)
{ 
    $airdate = [datetime]'2008-1-1';
    for($se=0; $se -lt 6; $se++) #six seasons - yeah i know...who wants to see them escape from the prison or island again!
    { 
        for($e=0; $e -lt 24; $e++) #Jack Bauer is my hero!
        { 
            $spitem = $list.Items.Add() 
            $spitem["Title"] = $shows[$s];
            $spitem["Season_x0020_Number"] = $se;
            $spitem["Episode_x0020_Number"] = $e;
            $spitem["Air_x0020_Date"] = $airdate;
            $spitem.Update() 
            $airdate = $airdate.AddDays(7);
        }
    }
} 

Bearing in mind that this script will create 9 sets of 6 season 24 episode shows (1296 schedules)...now think about what I've hooked up here on an ItemAdded Event:

  1. Create me a new page based on a given PageLayout
  2. Add a Content By Query Web Part to the page and filter the schedules.

These base pages would allow Users to then add their own extra content about this show using Web Parts, comment on the show etc. etc. in a true Social Collaborative environment.

I ran this on my Windows 2003 laptop in a MOSS development environment with SQL 2005, Visual Studio 2005 and Microsoft Office 2007 (yes that's right I keep things simple on my personal PC using mainly web based tools).

The results...well all 1296 schedules were created in the list fine, all 1296 pages with a web part on them were also created...eventually after about 10 minutes of my PC at 100% processor.

"What's your point Jeremy?"

Well, I remember about four years ago working in a web shop with some excellent .NET developers and trying to introduce some new tools that would save us time. They all looked great when we ran prototypes with a few bits of data and a couple of developers hitting it. But when we released it...it ran like a dog and we ended up coding it how we normally did from the roots up to get better performance.

I don't think SharePoint is going to have that scary a story...but I think the way that Microsoft are currently trying to encourage .NET developers over to the SharePoint fold may be a bit premature as the ones that have already taken the punt and moved across are still hand cutting things and still finding out the intricacies and limits of the platform. It may have been a better idea to get the Developer tools up to speed first and solve the issues around Continuous Integration (Deployments, Builds, Unit Testing etc.) that .NET developers expect from a development platform.

I think Microsoft need to be clearer on where SharePoint is a good fit and where it clearly isn't. Don't give the Business another reason to ask why things are taking so long to develop now we have a platform that does it all for us and will "saves months of development time". I'll save the InfoPath vs. ASPX forms for another time.

Believe it or not, I am a SharePoint fan and I can see where they are coming from with the 10 things about SharePoint. As my series of posts has pointed out...it may look great in the interface to be able to create a List, add a Web Part...but when you want to start automating things or hooking in Workflow and Event Receivers you're on a completely other level.

STSDev and other tools have come a long way in the last few months in making our lives easier as SharePoint Developers. I'll continue on this series of posts to cover the 10 areas that the site has covered on the same theme...but sorry they'll be no sexy silverlight rollovers on my posts ;-)

Published: 6/9/2008  8:22 AM | 3  Comments | 0  Links to this post

Jun 072008

Solution Development for SharePoint 2007 Part 7

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

"Event Receivers and Page Creation"

In the last week or so I've been doing a lot of work with creating pages with content on it automatically based on event receivers firing when items are created on Lists. To follow on with the TV show Schedule scenario when a new schedule is created, I want to create a page in the Pages Library on the Site itself and add a Content By Query Web Part similar to the one on the homepage of the Site, but with a filter on the Show itself. So each episode will have a page and it will link to all other episodes created.

This is done in two steps, firstly adding the Event Receiver to the Schedule List and then creating the Event Receiver.

I added the Event Receiver code to the Web scoped Feature Receiver FeatureActivated method. This can also be done in the definition of the ListInstance, but as I discussed before, because I will be cleaning up after myself I keep the adds and deletes in the same place for consistency. For the purposes of this tutorial I'm not doing anything but an ItemAdded, but you could have triggers on editing the Schedule or deleting it too to clean up pages created etc.

web.Lists["Schedule"].EventReceivers.Add(
  SPEventReceiverType.ItemAdded,
  "jeremythake.tvshowschedulesite, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8eb9a930004f2f1a",
  "jeremythake.tvshowschedulesite.ScheduleListEventReceiver");

I added the delete code to the FeatureDeactivating method as below. Now there is a risk that other Event Receivers could be added here so you may wish to check you delete the correct ones you've added.

for(int i=0; i<web.Lists["Schedule"].EventReceivers.Count; i++)
    web.Lists["Schedule"].EventReceivers[i].Delete();

The actual Event Receiver code is a simple C# class (.cs) in the root of the STSDEV project. The base of this is:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Diagnostics;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.Publishing.WebControls;
using System.Globalization;
 
namespace jeremythake.tvshowschedulesite
{
    public class ScheduleListEventReceiver : SPItemEventReceiver
    {
        public override void ItemAdded(SPItemEventProperties properties)
        {
            SPListItem currentListItem = properties.ListItem;
            using (SPWeb web = properties.OpenWeb())
            {
 
            }
        }
    }
}
 

From here you can start doing the work to create the new Page. There were some teething problems in getting the Page title to appear correctly in the navigation and getting access to what is easy in the Interface as the Page Title by going to the Page Settings page:

image

Because the Page is in the Pages List, you have to access the List Item Properties to change this...setting the PublishingPage Title Property doesn't set it. This took a little time to fathom out!

PublishingWeb pw = PublishingWeb.GetPublishingWeb(web);
 
//Get List Template - used below
SPListTemplateCollection listTemplates = web.Site.RootWeb.ListTemplates;
SPListTemplate scheduleListTemplate = listTemplates[scheduleListTemplateName];
 
// Create the new page in the PublishingWeb.
PublishingPageCollection pages = pw.GetPublishingPages();
PageLayout[] pageLayouts = pw.GetAvailablePageLayouts();
PageLayout blankWebPartPageLayout = null;
foreach (PageLayout pageLayout in pageLayouts)
    if (pageLayout.Title == "Blank Web Part Page")
        blankWebPartPageLayout = pageLayout;
PublishingPage newPage = pages.Add(scheduleName + ".aspx", blankWebPartPageLayout);
string checkInComment = "Page created";
//Title modified here
newPage.ListItem["Title"] = scheduleName;
newPage.ListItem.Update();
newPage.CheckIn(checkInComment);

and then putting the web part on the page:

SPFile categoryPageFile = web.GetFile("Pages/" + scheduleName + ".aspx");
if (categoryPageFile.Exists)
{
    categoryPageFile.CheckOut();
    using (SPLimitedWebPartManager webPartManager = categoryPageFile.GetLimitedWebPartManager(PersonalizationScope.Shared))
    {
        ContentByQueryWebPart contentByQueryWebPart = new ContentByQueryWebPart();
        contentByQueryWebPart.Title = "Related Schedules";
        contentByQueryWebPart.WebUrl = web.Url;
        contentByQueryWebPart.BaseType = string.Empty;
        contentByQueryWebPart.ServerTemplate = Convert.ToString((int)scheduleListTemplate.Type, CultureInfo.InvariantCulture);
        contentByQueryWebPart.CommonViewFields = "Air_x0020_Date,AirDate;Episode_x0020_Number,Episode;Season_x0020_Number,Season";
        contentByQueryWebPart.ItemStyle = itemStyle;
        //filter by schedule
        contentByQueryWebPart.FilterField1 = web.Lists[listName].Fields["Title"].Id.ToString("B");
        contentByQueryWebPart.Filter1ChainingOperator = ContentByQueryWebPart.FilterChainingOperator.Or;
        contentByQueryWebPart.FilterOperator1 = ContentByQueryWebPart.FilterFieldQueryOperator.Contains;
        contentByQueryWebPart.Filter2ChainingOperator = ContentByQueryWebPart.FilterChainingOperator.And;
        contentByQueryWebPart.FilterValue1 = scheduleTttle;
        contentByQueryWebPart.FilterType1 = "Text";
        contentByQueryWebPart.ShowUntargetedItems = false;
        webPartManager.AddWebPart(contentByQueryWebPart, "Right", 0);
    }
    categoryPageFile.CheckIn("Added Page");
    categoryPageFile.Publish("Published");
}

Again, this can be extended to start showing further web parts as they are developed such as Character guides or RSS feeds from fan sites etc.

I've uploaded the zipped project again for you guys to have a look at here.

Published: 6/7/2008  8:27 PM | 1  Comment | 1  Links to this post

Jun 072008

Solution Development for SharePoint 2007 Part 6

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

So far I've shown you guys how to deploy web parts and add them to pages with all of the related files. What I haven't shown you is how to deploy these things in a UAT or Production environment where STSDEV in Visual Studio won't be available.

So how should it be done? Well there are a few approaches, but by far the newest and coolest way is to use Powershell because it gives you all the advantages of the command prompt and batch files but also gives you access to the API. There's plenty out there to get you started with Powershell and I was amazed at the power of it...it's saved me heaps of time in debugging because you can simply use it to inspect objects without boosting up and attaching to the process.

Once you've installed Powershell the first thing I did was create a new .ps1 script and put it in my DeploymentFiles folder and add these lines of script:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

Then you have full access to the API...so I can create a SPSite object:

$weburl = "http://jt-asuslaptop"

$web=new-object Microsoft.SharePoint.SPWeb($weburl)

Scripts

Zach Rosenfield has some great scripts already written to access certain objects which I'll use. These are more intuitive and I've placed them in my default Powershell profile file which loads automatically when you start Powershell.

$weburl = "http://jt-asuslaptop"

$web = global:Get-SPWeb($weburl, $null)

Execution Policy

There is alot out there on this subject so I'll leave it to the Powershell pros, but basically you need to change the policy to get it to run scripts. I simply ran to give me god mode on my developer machine (obviously on production you'd want to be a bit more careful):

set-executionpolicy unrestricted

Inspecting Objects

Fire this off will give you heaps of information back on the SPWeb object...too much to copy and paste here...but what you can start doing is using the format-table function to output selected things:

$web | ft Title, Url

This will give you more targeted information:

Title Url

----- ---

Home http://jt-asuslaptop

So you can see the power in this for inspecting properties etc.

Firing methods

The other thing it is really great for is firing methods...for instance deleting SPWebs. As I discussed in a previous article, stsadm –o deleteweb won't work if the web has subweb's so you need to wipe them all recursively. The nice way of doing this is below...which only deletes the subwebs recursively:

############################################

 

# Delete Web -url <url>

 

############################################

function global:Delete-SPWeb($url){

$web = global:Get-SPWeb($url, $null)

foreach($subweb in $web.Webs)

{

global:Delete-SPWeb($subweb.Url);

$subweb.Delete();

}

$web.Delete();

}

Populating Lists with Test Data

The other great thing is populating lists with test data, these rigs will save you heaps of time with postbacks in interface...no one can enter data as quick as this:

#create some sample categories

$spCategoryList = $web.Lists["Categories"]

$categories = "Travel","Recruitment","The Way We Work"

foreach($category in $categories)

{

  $spitem = $spCategoryList.Items.Add()

  $spitem["CategoryName"] = $category

  $spitem.Update()

}

I'll be posting more full examples of this stuff very shortly that shows off more of the Powershell arsenal!

Published: 6/7/2008  12:49 AM | 0  Comments | 1  Links to this post

Jun 072008

Solution Development in SharePoint 2007 Part 5

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

So the next part in my series will basically show you how to deploy a Content By Query Web Part (CQWP) and also it's associated elements. The screenshot below shows the structure of my project file now. The Solution package can be downloaded here.

 

Deploying the Content By Query Web Part

As I mentioned in my previous article, there are various ways of deploying the web part but I feel you have more control by using the API in the code behind rather than using the <Module> element method, there's plenty of links I've collected over on my diigo page. As discussed in my previous web cast, it is important to use the CommonViewFields property to set the Fields that are available in the XSLT.

public override void FeatureActivated(SPFeatureReceiverProperties properties) {

using (SPSite site = new SPSite("http://jt-asuslaptop/"))

{

using (SPWeb web = properties.Feature.Parent as SPWeb)

{

SPFile file = web.GetFile(web.Url + "/default.aspx");

SPLimitedWebPartManager webpartsMng = file.GetLimitedWebPartManager(PersonalizationScope.User);

ContentByQueryWebPart contentByQueryWebPart = new ContentByQueryWebPart();

contentByQueryWebPart.Title = "Schedule";

contentByQueryWebPart.WebUrl = web.Url;

 

SPListTemplateCollection listTemplates = web.Site.RootWeb.ListTemplates;

SPListTemplate template = listTemplates["Schedule"];

contentByQueryWebPart.BaseType = string.Empty;

contentByQueryWebPart.ServerTemplate = Convert.ToString((int)template.Type, CultureInfo.InvariantCulture);

contentByQueryWebPart.CommonViewFields = "Air_x0020_Date,AirDate;Episode_x0020_Number,Episode;Season_x0020_Number,Season";

contentByQueryWebPart.ItemStyle = "ScheduleStyle";

contentByQueryWebPart.ItemXslLink = site.Url + "/Style%20Library/jeremythake/schedule.xsl";

webpartsMng.AddWebPart(contentByQueryWebPart, "Right", 0);

}

}

}

 

Cleaning up

Cleaning up after yourself will keep your mum from lecturing you on the finer art of bed making! So with that in mind you should delete the web parts that you've installed on deactivating the feature. All the other stuff is automatically cleaned up such as style sheets etc.

 

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {

using (SPWeb web = properties.Feature.Parent as SPWeb)

{

SPFile file = web.GetFile(web.Url + "/default.aspx");

SPLimitedWebPartManager webpartsMng = file.GetLimitedWebPartManager(PersonalizationScope.Shared);

for (int i=0; i< webpartsMng.WebParts.Count; i++)

webpartsMng.DeleteWebPart(webpartsMng.WebParts[i]);

}

}

 

Writing the style sheet

The style sheet is where all the rendering magic happens. I use the ListAll xsl:template (configured in the ItemStyle property of the ContentByQueryWebPart) to render all the Fields to ensure that I've got the correct property names such as @Episode_x005F_x0020_Number which is not how it was added in CommonViewFields property e.g. Episode_x0020_Number,Episode.

 

Again, I noticed some formatting issues in the Number and DateTime and found some functions which I've diigo'd.

 

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet

version="1.0"

exclude-result-prefixes="x d xsl msxsl cmswrt"

xmlns:x="http://www.w3.org/2001/XMLSchema"

xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"

xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

 

    <xsl:template name="ListAll" match="Row[@Style='ListAll']" mode="itemstyle">

        <xsl:for-each select="@*">

            P:<xsl:value-of select="name()" />

        </xsl:for-each>

    </xsl:template>

 

    <xsl:template name="ScheduleStyle" match="Row[@Style='ScheduleStyle']" mode="itemstyle">

        <h5>

            <xsl:value-of select="@Title" />

        </h5>

        Season: <xsl:value-of select="format-number(@Season_x005F_x0020_Number,'##')" />

        - Episode: <xsl:value-of select="format-number(@Episode_x005F_x0020_Number,'##')" />

        - Air Date: <xsl:value-of disable-output-escaping="no" select="ddwrt:FormatDate(string(@Air_x005F_x0020_Date), 3081, 1)" />

    </xsl:template>

 

</xsl:stylesheet>

 

Deploying the style sheet file

Linking to a custom style sheet actually is best practice due to not modifying the ItemStyle.xsl file as this is part of the base install of SharePoint and there is a risk of patches or new versions overriding this. The below code in the Feature Manifest file (elements.xml) allows the deployment of files pointed to in the Path folder attribute to a defined url by the URL attribute. I placed this in the Site Collection scoped feature.

 

    <Module Name="JeremyThakeStyles" RootWebOnly="TRUE" Path="Styles" Url="Style Library/jeremythake">

        <File Url="schedule.xsl" IgnoreIfAlreadyExists="TRUE" Type="GhostableInLibrary" />

    </Module>

 

The other neat thing about STSDEV is that you don't have to create .ddf files manually and so adding the .css file into the Visual Studio Project automatically includes it in the Solution .wsp package. You then only have to add an entry into the Feature file itself.

 

<ElementManifests>

        ...

        <ElementFile Location="Styles\schedule.xsl"/>

    </ElementManifests>

 

"There's one small problem!"

Although this all seems great, the line where I set the ItemXslLink throws an error when it's set...

 

Unable to display this Web Part. To troubleshoot the problem, open this Web page in a Windows SharePoint Services-compatible HTML editor such as Microsoft Office SharePoint Designer. If the problem persists, contact your Web server administrator.

 

I've searched for solutions to this and can't seem to find the answer. The XSL path definitely is correct and I can get to "Modify My Web Part" without error. If anyone has any suggestions I'd greatly appreciate it.

"The others"

On my blogging travels I've found some great articles from other awesome SharePoint Experts.

Michael Blumenthal's post on "what do I give to my SharePoint Admin" poses a great question that often comes up from organisations of a size where they have someone who owns the Farm and won't let anyone else near it like a true guard dog! Every farm should be dealt with in this nature and a UAT environment should be in place to ensure that deployments work correctly before doing this in Production. The methods Michael uses are heading more towards Continuous Integration and having a Build server to automatically deploy the packages from source control built projects.

Isahi Sagi also wrote a good article on "planning a SharePoint solution" and point 8 again mentions issues around the fact that not everything can go in the .WSP package, for example InfoPath forms are deployed using stsadm commands. Following on from this, rather than having to type all the stsadm commands in it's better to provide the SharePoint Admin with a script to run to deploy the .wsp package. I'll discuss this in more detail in another post.

Chris O'Briens article has some great samples of Solution Packages. He's doing some great work with SPConfigStore on CodePlex also so check it out. There are lots of approaches to managing configuration per environment. Again, something else I'll be discussing...

Serge van den Oever shined some good light on the Solution Generator for exporting Site definitions and points out some interesting facts about what is exported.

There is also some good presentations from DevConnections too which give a good overview of Solutions.

Waldek Mastykarz also has been busy blogging around this space and seems to be a very good contributor to the MSDN Forums around this too. Waldek makes some great points about big WCM projects where you just simply wouldn't want to script the whole thing as a Feature because it would simply take too long, definitely worth a read on his thoughts on Deployment.

Obviously every man and his dog has blogged about VSeWSS 1.3 so if you hadn't heard about it...you know now ;-)

Published: 6/7/2008  12:02 AM | 5  Comments | 1  Links to this post

Jun 062008

Solution Development in SharePoint 2007 Part 4

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

Adding extra assemblies to your Solution

Just had to post about this because it's been driving me crazy. I've got various Solutions now that have been built using STSDEV 1.3 in Visual Studio 2008. There are common classes I've been using and was scratching my head to get the dll to deploy with the solution and was hacking it by installing it directly into the GAC! Now I've worked it out and full credit to the STSDEV guys for this feature!

Basically, add the dll Reference to your Solution Project and then right click on this dll and ensure that the 'Copy Local' property is set to True – this will place it into the \bin\debug folder.

Then simply go to your Solution.xml file in the Deployment folder:

And simply copy the one instance of the <Assembly> element:

<Assembly Location="CPMining.Portal.Users.DocumentManagementSite.dll" DeploymentTarget="GlobalAssemblyCache">

<SafeControls>

<SafeControl Assembly="CPMining.Portal.Users.DocumentManagementSite, Version=1.0.0.0, Culture=neutral, PublicKeyToken=742400bf5d0071a5" Namespace="CPMining.Portal.Users.DocumentManagementSite" TypeName="*" Safe="True" />

</SafeControls>

</Assembly>

And then modify it to your dll you want to reference:

<Assembly Location="CPMining.Portal.Common.dll" DeploymentTarget="GlobalAssemblyCache">

<SafeControls>

<SafeControl Assembly="CPMining.Portal.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=742400bf5d0071a5" Namespace="CPMining.Portal.Common" TypeName="*" Safe="True" />

</SafeControls>

</Assembly>

When you build the project now and it generates your .ddf and .wsp you'll notice that it now picks up the new .dll! Awesome! Saved me lots of time with caching and encourages reuse of code among Solution Packages! This will also help you out in terms of deploying third party .dlls such as Telerik which we have started to deploy to farms and can now be down via Solution Packages!

Published: 6/6/2008  11:09 PM | 0  Comments | 0  Links to this post

Jun 042008

Solution Development in SharePoint 2007 Part 3

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

As promised I've uploaded the 3rd webcast to show how Chapter 3 is implemented. I've also posted the sample code below for you guys to pick and chose as you please. Click here to watch the webcast (NEW 32MB swf - 40mins) .

The .zip of the solution can be downloaded here.

Site Collection Feature.xml

<?xml version="1.0" encoding="utf-8"?>

<!--Created by STSDEV at 4/06/2008 8:39:34 PM-->

<Feature

Id="AE49F2FB-1B45-483A-A8C4-DFD698780ACA"

Title="A sample feature: jeremythake.tvshowschedule"

Description="This SharePoint solution was created by the STSDEV utility (http://codeplex.com/stsdev)"

Version="1.0.0.0"

Scope="Site"

Hidden="false"

ImageUrl="jeremythake.tvshowschedule\AfricanPith32.gif"

ReceiverAssembly="jeremythake.tvshowschedule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8eb9a930004f2f1a"

ReceiverClass="jeremythake.tvshowschedule.FeatureReceiver" xmlns="http://schemas.microsoft.com/sharepoint/">

<ElementManifests>

<ElementManifest

Location="elements.xml" />

     <ElementManifest

        Location="schedule\listdefinition.xml" />

     <ElementFile Location="schedule\allitems.aspx"/>

     <ElementFile Location="schedule\dispform.aspx"/>

     <ElementFile Location="schedule\editform.aspx"/>

     <ElementFile Location="schedule\ListDefinition.xml"/>

     <ElementFile Location="schedule\newform.aspx"/>

     <ElementFile Location="schedule\schema.xml"/>

</ElementManifests>

</Feature>

Things to note:

  • I manually added the <ElementFile>'s for the ListTemplate generated by Solution generator.
  • I also added the <ElementManifest> for the ListTemplate also

Site Collection Elements.xml

<?xml version="1.0" encoding="utf-8"?>

<!--Created by STSDEV at 4/06/2008 8:39:34 PM-->

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

    <Field Type="Number" DisplayName="Season Number" Required="FALSE" Group="jeremythake" ID="{3ae02cdc-b29e-43f4-99e6-96fa1eca9c3f}" SourceID="{7d46c011-b7e1-406f-873e-24af7b2f4fbb}" StaticName="Season_x0020_Number" Name="Season Number" xmlns="http://schemas.microsoft.com/sharepoint/">

    </Field>

    <Field Type="Number" DisplayName="Episode Number" Required="FALSE" Group="jeremythake" ID="{70b38d61-0241-4d45-af60-df23d458567c}" SourceID="{7d46c011-b7e1-406f-873e-24af7b2f4fbb}" StaticName="Episode_x0020_Number" Name="Episode Number" xmlns="http://schemas.microsoft.com/sharepoint/">

    </Field>

    <Field Type="DateTime" DisplayName="Air Date" Required="FALSE" Format="DateOnly" Group="jeremythake" ID="{e48d08af-a1ce-4647-be18-3360f3a2ce17}" SourceID="{7d46c011-b7e1-406f-873e-24af7b2f4fbb}" StaticName="Air_x0020_Date" Name="Air Date" xmlns="http://schemas.microsoft.com/sharepoint/">

    </Field>

    

    <ContentType ID="0x0100608C182017B2664787866C9B81660001" Name="Schedule" Group="jeremythake" Version="3" xmlns="http://schemas.microsoft.com/sharepoint/" Hidden="False" ReadOnly="False" ResourceFolder="Schedule" Sealed="False">

        <Folder TargetName="_cts/Schedule" />

        <FieldRefs>

            <FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" />

            <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />

            <FieldRef ID="{3ae02cdc-b29e-43f4-99e6-96fa1eca9c3f}" Name="Season_x0020_Number" />

            <FieldRef ID="{70b38d61-0241-4d45-af60-df23d458567c}" Name="Episode_x0020_Number" />

            <FieldRef ID="{e48d08af-a1ce-4647-be18-3360f3a2ce17}" Name="Air_x0020_Date" />

        </FieldRefs>

    </ContentType>

</Elements>

Things to note:

  • This was a simple case of adding the <Field>'s and <ContentType> generated from the Feature Generator application

Site Collection ListDefinition.xml

<?xml version="1.0" encoding="utf-8"?>

<Elements Id="ebc1f1f6-5f70-4771-ab22-608c6e0ae005" xmlns="http://schemas.microsoft.com/sharepoint/">

<ListTemplate Name="Schedule" DisplayName="Schedule" Description="" BaseType="0" Type="100" OnQuickLaunch="TRUE" SecurityBits="11" Sequence="410" Image="/_layouts/images/itgen.gif" />

</Elements>

Things to note:

  • This was a auto generated by the Solution Generator tool (with VSeWSS)

Site Collection Schema.xml

<?xml version="1.0" encoding="utf-8"?>
<
List Name="Schedule" Title="Schedule" Description="" Direction="0" BaseType="0" Url="Lists/Schedule" FolderCreation="FALSE" EnableContentTypes="TRUE" Type="100" Id="ebc1f1f6-5f70-4771-ab22-608c6e0ae005" xmlns="http://schemas.microsoft.com/sharepoint/">

<MetaData>

<Views>

...

</Views>

</Fields>

...

</Fields>

<ContentTypes>

<ContentTypeRef ID="0x0100608C182017B2664787866C9B81660001" />

<ContentTypeRef ID="0x0120" />

</ContentTypes>

<Forms>

<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main" />

<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main" />

<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main" />

</Forms>

</MetaData>

</List>

Things to note:

  • This was a auto generated by the Solution Generator tool (with VSeWSS)
  • Had to delete <ContentType> and add <ContentTypeRef>

Web Feature.xml

<?xml version="1.0" encoding="utf-8"?>

<!--Created by STSDEV at 4/06/2008 8:39:34 PM-->

<Feature

Id="5FF56A65-A901-4C4B-AFA8-BA721C98712B"

Title="A sample feature: jeremythake.tvshowscheduleweb"

Description="This SharePoint solution was created by the STSDEV utility (http://codeplex.com/stsdev)"

Version="1.0.0.0"

Scope="Web"

Hidden="false"

ImageUrl="jeremythake.tvshowschedule\AfricanPith32.gif"

ReceiverAssembly="jeremythake.tvshowschedule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8eb9a930004f2f1a"

ReceiverClass="jeremythake.tvshowschedule.WebFeatureReceiver" xmlns="http://schemas.microsoft.com/sharepoint/">

<ElementManifests>

<ElementManifest

Location="elements.xml" />

</ElementManifests>

</Feature>

Web Elements.xml

<?xml version="1.0" encoding="utf-8"?>

<!--Created by STSDEV at 4/06/2008 8:39:34 PM-->

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

    <ListInstance

        Title="Schedule"

        FeatureId="AE49F2FB-1B45-483A-A8C4-DFD698780ACA"

        OnQuickLaunch="TRUE"

        TemplateType="100"

        Url="Schedule" />

</Elements>

Things to note:

  • This was manually written and they're all pretty similar. The intellisense with VSeWSS will make this easier.

Web FeatureReceiver.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

 

using Microsoft.SharePoint;

using Microsoft.SharePoint.WebControls;

using Microsoft.SharePoint.WebPartPages;

using Microsoft.SharePoint.Navigation;

using Microsoft.SharePoint.Administration;

using System.Globalization;

using Microsoft.SharePoint.Publishing.WebControls;

 

namespace jeremythake.tvshowschedule {

public class WebFeatureReceiver : SPFeatureReceiver {

 

public override void FeatureActivated(SPFeatureReceiverProperties properties) {

//Add CQWP

using (SPSite site = new SPSite("http://jt-asuslaptop/"))

{

using (SPWeb web = site.AllWebs["Test"])

{

SPFile file = web.GetFile(web.Url + "/default.aspx");

SPLimitedWebPartManager webpartsMng = file.GetLimitedWebPartManager(PersonalizationScope.User);

ContentByQueryWebPart contentByQueryWebPart = new ContentByQueryWebPart();

contentByQueryWebPart.Title = "Schedule";

contentByQueryWebPart.WebUrl = web.Url;

 

SPListTemplateCollection listTemplates = web.Site.RootWeb.ListTemplates;

SPListTemplate template = listTemplates["Schedule"];

contentByQueryWebPart.BaseType = string.Empty;

contentByQueryWebPart.ServerTemplate = Convert.ToString((int)template.Type, CultureInfo.InvariantCulture);

contentByQueryWebPart.CommonViewFields = "Air_x0020_Date,AirDate;Episode_x0020_Number,Episode;Season_x0020_Number,Season";

contentByQueryWebPart.ItemStyle = "ScheduleStyle";

 

webpartsMng.AddWebPart(contentByQueryWebPart, "Right", 0);

}

}

}

 

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {

/* no op */

}

 

public override void FeatureInstalled(SPFeatureReceiverProperties properties) {

/* no op */

}

public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {

/* no op */

}

}

}

Things to note:

  • This was manually written. But you can use this as a start and modify it from here. I'll be writing more complicated ones in future posts.

 

 

Published: 6/4/2008  8:05 AM | 2  Comments | 82  Links to this post

Jun 032008

Solution Development in SharePoint 2007 Part 2

Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8

"Previously on 'Prison Break'...I mean 'Solution Packages for Developers'"

My blog got quite a few extra visitors over the last couple of days since writing about SharePoint Development and "doing things the right way". A few people have been giving me "gentle" and "subtle" hints to start a series and based on the success of Paul Culmsee's various part articles I've decided to give him a run for his money...but in a different space to his primarily focused on Developer land.

"Telling a real 'SharePoint Developer' from a 'SharePoint User'"

So as I was saying in my previous post...there is this notion of SharePoint Solution Packages with Features. These Solutions can be deployed to the SharePoint Farm no matter how big or small. The beauty of this is that it allows you to write these packages in Visual Studio in XML and .NET code which can be put into source controll and gives a paradigm of a snapshot of a "Solution" that is deployed in an environment.
The dangers of SharePoint are very apparent, any business user can create whatever they like if they are given Owner rights and before they know it, have thrown Excel in the bin to run their business in, and have lots of SharePoint Lists (SPList) and plenty of SharePoint Designer (SPD) workflows running. Imagine after four months, the User comes to you and says "I'd quite like it if it could do this". So any half respectable Developer would backup the SharePoint Site (Web) or SiteCollection (Site) that it has been set up in and then restore it in their development environment. The Developer makes his changes in the SharePoint interface and shows it to the User and they love it and want it in Production that afternoon...after all they were so close and it didn't take more than half a day to do their bit.

The Developer can't backup and restore into Production because Production has been in use since they last did their backup and it would mean losing data. Did the Developer write down every last interface step he did to get to the point he's at now? Does he really want to do that all over again in UAT environment for handover? Does he then really want to do that all over again in Production? How long are these steps going to take? Is there a risk of forgetting something or configuring it incorrectly? Is this going to mean more than 10 minutes down time in Production and therefore out of hours work...you bet your last Rolo it does!

"No it won't take the same amount of time to create it in a Solution!"

So SharePoint gave us Solutions to allow us to guarantee that whatever is installed in one environment is the same as the next. Wouldn't it be nice if you could just point at the Development environment and click "create me a solution for all of this". "Yes, Jeremy, it'd be really nice". OK...now we're past the unrealistic expectations of Microsoft lets get with reality.

There is a platform here to do these things and there is considerable SDK documentation on the APIs....there isn't considerable documentation and samples for using the APIs. If you do a search for "SPLimitedWebPartManager" in Google/Live and see how many results come back that aren't SDK API pages (diigo) you'll notice hardly any results. This Manager is one of the core ways of adding/removing/modifying Web Parts on a page. For me this is one of the most obvious, key parts of any Solution Deployment you would try and write to automate your work. The samples go as far as deploying the Web Part to the Web Part Gallery, but do not show you how to easily add them to the page or modified existing ones or delete ones you don't require.

"Next Week on 'Battlestar Gallactica', I mean 'Solution Packages for Developers'" 

In the next few weeks (before I jet set away for 3 weeks - don't worry I'll be back with more) I'll be posting some key scenarios that are done every day in the SharePoint User Interface, but replicated as deployable Solutions. I'll be using the tools mentioned in the last post and pointing to various resources for further information.

In the beginning...

To get your taste buds rolling, I thought I'd get started with one...and no I'm not doing "Hello World!". I'm going to show you how to deploy a SharePoint List (ListTemplate and ListInstance) completely in a Solution and I'll even throw in a Content By Query Web Part to display some data. Content By Query Web Parts are awesome, period (diigo). They can do so much...as Andrew Coates was saying at the last SharePoint User Group in Perth..."Leverage the Platform". Why write a web part in custom code to render a SharePoint List with a filter when you can use what's there OOTB. Yes I know it has it's limitations and I know Sezai will be throwing things at me next time he sees me. But seriously...lets not make things complicated "cos I can"..."Keep It Simple Stupid!" (KISS). They are a great way to leverage information by using Content Types and are proven to perform - Waldek Mastykarz clearly has more time on his hands than me to get this amount of evidence on it! I also have to give heaps of credit to "Mr STSADM" (Gary Lapointe) for giving plenty of sample SharePoint API codes which kick started in the right direction as there was hardly anything out there on this

"Chapter One – Business User Land" 

The Business User creates a List in a Site to store information on TV Shows with their "Season Number", "Episode Number" and "Air Date". The homepage of the Site will display the List in a web part along with other web parts not related to this project.

Now in the SharePoint User Interface the User would go to Site Actions, click on Create, select Custom List, create their list, create the columns, adding the List View Web Part, etc. Maybe ringing the Development team for support at various stages and expect instant help. I've uploaded a recording of this in action.

Resources needed

Business Users (5 minutes)

  • Create List with custom Columns
  • Create List View Web Part

"Chapter Two – Designer Land"

The Business User has gone to the IS department and isn't happy with how it is being rendered and wants to move away from a list like display to have headings and information render in a specific way.

So now the Designer goes away and takes a look at the List and creates a Content By Query Web Part and uses XSLT to render it how the Business User has asked. I've uploaded a recording of this in action also.

Resources needed

Designer (15 minutes)

  • Creating xsl:template elements for stying Content By Query Web Part and formatting the custom columns
  • Modifies the Content By Query Web Part .wepart XML to expose the custom columns and re-imports it

"Chapter Three – Developer Land"
So now the Business are really happy with this, but so far this has all happened in Production system and hasn't allowed Designers the ability to test that the rendering is going to work or that if they needed to add new columns etc it hasn't affected the Businesses day to day running. Chapter Three, allows you to get to this scenario.

In Developer land I would advise doing the same thing in some circumstances as the first two Chapters, even when you are a dab hand at coding straight away, but there are some design decisions to make at this stage.

Design Decisions

  1. Site Content Types (and Site Field Types) vs. List Instance Columns
    Columns will mean you cannot chose the List columns in the Content By Query Web Part when sourcing, grouping and filtering also may be re-use in Field Types in later developments.
  2. List Templates (with a List Instance) vs List Instance
    List Instance will mean losing the ability to customise the Forms used with the List (in following posts I'll explain this further) – you'd simply have a ListInstance with Rows defined and no hook up to Content Types or custom forms and inherently workflow or records policies also.
  3. Content By Query Web Part vs. List View Web Part
    List View Web Part won't allow for complete custom rendering to look non-SharePoint like...also can create custom CAML queries to filter and sort data in a more powerful way and also source data from multiple Lists over multiple sites over multiple Content Types.
  4. SPLimitedWebPartManager vs. AllWebParts
    The advantage of using the API in the FeatureActivated event rather than using the Module Element of the Elements Manifest. I find there is more control and also, exporting the .webpart XML hard codes the List Guids which can be dynamically entered using the API.

NOTE: I'll be putting up a webcast of this chapter tomorrow evening with full source code listings.

So, the Solution requires the developer to create a Site (SPWeb) using the "Publishing Site" Site Template called "TV Show Schedule". The Site will have a SharePoint List Instance (SPList) with the ability to add a List Item (SPListItem) of a "Schedule" Content Type with "Season Number" (Int), "Episode Number" (Int) and "Air Date" (DateTime) extra column metadata (SPField) based on a created List Template (ListTemplate). The front default.aspx page (SPFile) will have a Content By Query Web Part that will display these items Grouped By "Title" and Sorted by "Air Date".

Resources needed

Designer (15 minutes)

  • Creating xsl:template elements for stying Content By Query Web Part and formatting the custom columns

Developer (2 hours)

  • Create a ListTemplate set of creation scripts
  • Create a Content Type set of creation scripts
  • Create a Field Type set of creation scripts
  • Create a Content By Query Web Part to render on default.aspx
  • Create a Solution package to deploy the Solution Feature to the Site Collection
  • Create a install script to deploy the Solution to the Farm, create the new site and activate the Feature

Walkthrough

Content Types (ContentType)

I would agree with doing it in the UI first for Content Types too, purely for the Content Type ID which intelligently stores its parent information within it and is just easier to create a new one in the interface and copy it yourself. I use CT Feature Creator as it creates XML quite easily very similar to Solution Generator for both Site Content Types and Site Columns.

SharePoint Lists (SPList, ListTemplate, ListInstance)

First things first we need to imitate what the Business User did in Chapter One in a programmatic way. As an example of this...run up Solution Generator which is part of the VSeWSS install and point it at the Schedule List that is OOTB when activating the publishing feature. You'll get a heap of files...the relevant ones are the ones in the folder named the same the List itself and will contain a schema.xml file along with other .aspx files such as Upload.aspx etc. Anyone who can put their hand up and can script a ListTemplate schema.xml with Views, Fields, Content Types, Security and Forms from scratch without any references needs help! Debugging these things when you have an error is hard and it doesn't give you line numbers of where you've screwed up...so with Lists, do them in the User Interface and generate them.

Stuff to change

  1. Solution Generator also doesn't give it a unique Type ID in the ListTemplate definition placed in the Manifest file – although it seems to work fine. It appears these have to be unique when attaching Event Receivers and appears to have to be unique and less than 1000 - this can be used if we want to include this list in a custom site definition.
  2. Solution Generator does add instances of ContentTypes in list rather than ContentTypeRef's..urgh! Same with Fields...urgh! So you need to manually change these.
  3. It will also create folders with spaces in it which will break STSDEV and the Solution package itself, so you'll have to rename your ListTemplate's accordingly. For some reason it doesn't have the same behaviour as SPField and give you x_20_x in replacement of a space...who needs consistency I say!
  4. Solution Generator doesn't work on Site Definitions other than WSS definitions.
  5. You'll also notice that if you script it with the UI interface content type then generate the Content Type from script that it won't match up as the <Fields> elements are instances on the list...the inheritance creates a copy. So it's best to create the Content Type first then install this as a Feature on the Site and then create a List and use Solution Generator to script it.

Content By Query Web Part (ContentByQueryWebPart)

Again, even with this one, it's easier to get a CBQWP going via the User Interface than it is to guess what the minimum properties of the API object is to get the damn thing running! Although when you start wanting to show non-OOTB columns you have to do some behind the scenes work (an MSDN article covers this) – again this is something I wouldn't expect a business user to be able to do. So the dangers here is that they use other Web Parts like ListViewWebPart without realising the power of the ContentByQueryWebPart.

Once that's all up and running and you want to script this you can spend the next half hour guessing what API Property Name maps to what Web Part Property in the User Interface!

"Chapter Four: You took how long to @!?%@ do this!?!?"

I'm sure we've all been there...Development Managers asking why it's taken us this long to do it when he can do it in the interface in 5 minutes (ie. Chapter One). So here's some ammo:

Time Saved in Deployment

Although there is a considerable amount of effort involved in writing the Solution compared to generating it all in the Interface...in the long run, on large team projects and multi-environment scenarios it wins hands down.

Guaranteed same end result

With it being scripted, it means that the same Solution will be created in each environment.

Enforces standards

By forcing Developers to work in this way, it also ensures that everyone is working the same way. Microsoft is pushing this as a standard and as training courses start appearing for more than just "boot camps" these methods will mature.

Self-Documentation and reducing resource dependencies

It also ensures that everything is documented because if it isn't in the Solution package...it doesn't get installed into the next environment in the chain. So rather than relying on the Developer who developed it to deploy it to UAT, anyone can run the Solution package install.

Source controlled

By having the Solution in Source Control it ensures that if the initial Developer is sick for a week or his machine dies that all the work he's been doing is backed up and accessible to others.

Up to speed quickly

Fresh Developers on the scene can, from a fresh SharePoint Development environment, start pulling down the Solutions from source control and installing them and seeing how they fit together. Furthermore, once they're up to speed can work on the project.

Debugging

By being able to recreate these Solutions in any SharePoint Farm means you can test them in isolation and also their dependencies. On top of this you can get environments up for debugging and stepping through code very quickly to separate solutions to find out what is causing the issues.

Script load and performance testing

For the more advanced projects, you can then also load test and performance test the solutions in UAT before pushing this into Production. For more on this go over to Paul Culmsee's "Why do SharePoint Projects Fail?" series.

"Chapter Five: Happy Ending"

The sooner you educate the Users that it isn't simply a case of "smashing it out" in Production the better:

  • Messy/un-versioned master pages, page layouts, itemstyles.xml etc.
  • No distinction in projects on site – grouping web parts, lists, styles, content types etc.
  • High risk of deployments screwing up
  • Better use for SharePoint Developers time!
  • Undocumented Solutions appearing in Farms

"Chapter Six: Until next time"

So now we have a complete Solution which will deliver our scenario. In the next post I will go onto talk about getting a bit more complicated with the Content By Query Web Part and deploying some XSL and CSS as part of the package to "spruce" the Web Part.    

References

Here are a few references to the core MSDN/Technet links that I've used...it's worthwhile checking out my Diigo links also.

Solution 101

SDK - http://msdn.microsoft.com/en-us/library/ms413687.aspx

SDK Schema - http://msdn.microsoft.com/en-us/library/ms442108.aspx

Technet - http://technet.microsoft.com/en-us/library/cc263289.aspx

Feature 101

Feature.xml

SDK - http://msdn.microsoft.com/en-us/library/ms439657.aspx

SDK Schema - http://msdn.microsoft.com/en-us/library/ms475601.aspx

Technet - http://technet.microsoft.com/en-us/library/cc263360.aspx

Element.xml

 Types - http://msdn.microsoft.com/en-us/library/ms474383.aspx

 SDK Schema - http://msdn.microsoft.com/en-us/library/ms414322.aspx

Site Templates

SDK - http://msdn.microsoft.com/en-us/library/ms434313.aspx

Other good articles

  • Andrew Connell and Rob Bogue are in the process of a series of web casts...more information here. Rob has also followed up from a Q&A after the web cast with even more valuable information!
  • Chris O'Conner has just posted a similar series to what I am going to be doing here also.
  • Heather Soloman also has some great articles on this area.
  • Eric Shupps also has some kind advice on the subject.
Published: 6/3/2008  7:15 AM | 4  Comments | 0  Links to this post