David Jacobus

SharePoint Consultant

  Home  |   Contact  |   Syndication    |   Login
  17 Posts | 0 Stories | 7 Comments | 0 Trackbacks

News

Hello,
My name is David Jacobus I am a former teacher of Computer Science and Mathematics, who is now working full time as a SharePoint Consultant!

Article Categories

Archives

Post Categories

Sunday, October 30, 2011 #

In the previous blog post I discussed working with the list import tool for creating lists which was a timesaver for developing lists in the UI and then importing that list into a Visual Studio solution. I had some time in-between projects to make this process for simple lists just using the default views. How about doing this entire process in about 15 minutes in code! This is such a time saver that I just had to blog about it and save other developers this time intensive task!

1. Use a utilities library for creating field’s and content types.

2. Create a feature receiver:

a. Create the fields

b. Create the Content Type

c. Create the list instance

My Utilities Library can be downloaded here

   1: using System;
   2: using System.Runtime.InteropServices;
   3: using System.Security.Permissions;
   4: using Microsoft.SharePoint;
   5: using Microsoft.SharePoint.Security;
   6: using System.Collections;
   7:  
   8: namespace Your Namespace
   9: {
  10:     /// <summary>
  11:     /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
  12:     /// </summary>
  13:     /// <remarks>
  14:     /// The GUID attached to this class may be used during packaging and should not be modified.
  15:     /// </remarks>
  16:  
  17:     [Guid("ebecc7c3-5d91-4bcf-a7d6-a332c786431e")]
  18:     public class YourClass_TypeEventReceiver : SPFeatureReceiver
  19:     {
  20:         // add the groupname for your content types and fields
  21:  
  22:         const string columnGroup = "GroupName";
  23:  
  24:         const string ctName = "ContentType";
  25:         // Uncomment the method below to handle the event raised after a feature has been activated.
  26:  
  27:         public override void FeatureActivated(SPFeatureReceiverProperties properties)
  28:         {
  29:             using (SPWeb spWeb = properties.GetWeb() as SPWeb)
  30:             {
  31:                 //add the fields
  32:                 addFields(spWeb);
  33:  
  34:                 SPContentType testCT = spWeb.ContentTypes[ctName];
  35:                 // we will not create the content type if it exists 
  36:          //you could use an else clause here to update the content type if 
  37:         //it exists
  38:                 if (testCT == null)
  39:                 {
  40:                     //the content type does not exist add it
  41:                     addContentType(spWeb, ctName);
  42:                     //create the list if the content type dosen't exist
  43:                     CreateAlertList(spWeb);
  44:                 }
  45:  
  46:             }
  47:  
  48:         }
  49:  
  50:  
  51:  
  52:   
  53:         public void addFields(SPWeb spWeb)
  54:         {
  55:            
  56:             Utilities.addField(spWeb, "AlertBody", SPFieldType.Note, true, columnGroup);
  57:             Utilities.addField(spWeb, "AlertDateTimeStart", SPFieldType.DateTime, false, columnGroup);
  58:             Utilities.addField(spWeb, "AlertDateTimeEnd", SPFieldType.DateTime, false, columnGroup);
  59:  
  60:  
  61:         }
  62:  
  63:  
  64:         private static void addContentType(SPWeb spWeb, string name)
  65:         {
  66:             SPContentType myContentType = new SPContentType(spWeb.ContentTypes["Item"], spWeb.ContentTypes, name) { Group = columnGroup };
  67:  
  68:             spWeb.ContentTypes.Add(myContentType);
  69:             addContentTypeLinkages(spWeb, myContentType);
  70:  
  71:  
  72:             myContentType.Update();
  73:         }
  74:  
  75:         public static void addContentTypeLinkages(SPWeb spWeb, SPContentType ct)
  76:         {
  77:          
  78:             Utilities.addContentTypeLink(spWeb, "AlertBody", ct);
  79:             Utilities.addContentTypeLink(spWeb, "AlertDateTimeStart", ct);
  80:             Utilities.addContentTypeLink(spWeb, "AlertDateTimeEnd", ct);
  81:         }
  82:         private void CreateAlertList(SPWeb web)
  83:         {
  84:  
  85:             Guid newListGuid = web.Lists.Add("PriorityAlerts", " Corporate Alert List.",
  86:  
  87:                 SPListTemplateType.GenericList);
  88:  
  89:             SPList newList = web.Lists[newListGuid];
  90:             newList.ContentTypesEnabled = true;
  91:             var news = web.ContentTypes[ctName];
  92:             newList.ContentTypes.Add(news);
  93:             newList.ContentTypes.Delete(newList.ContentTypes["Item"].Id);
  94:             newList.Update();
  95:             var view = newList.DefaultView;
  96:             //add all view fields here
  97:  
  98:             
  99:             view.ViewFields.Add("AlertBody");
 100:             view.ViewFields.Add("AlertDateTimeStart");
 101:             view.ViewFields.Add("AlertDateTimeEnd");
 102:             view.Update();
 103:  
 104:  
 105:  
 106:  
 107:         }
 108:         
 109:  
 110:  
 111:  
 112:     }
 113: }
 114:  

I can now create lists instances quickly that it is now a trivial task!  Of course much of the work is contained in the Utilities Library.


Tuesday, September 20, 2011 #

List Instance Woe’s

My last SharePoint Project had at least 20 custom list instances which were a pain point for a couple of us developers! There are a couple of ways to create custom list instances. Our Way:

1. We tied all of our lists to custom content types.

2. We created the custom fields and content types in code as feature receivers.

3. We created the custom list in the UI .

a. Added the content type

b. Modified the default view with the custom fields.

4. We then exported the site as a WSP and then imported the WSP into Visual Studio for one or more lists.

a. This gave us the Schema.xml

b. Pages module

i. This was the main cause of our problems!

ii. Here is an example for a navigation list

clip_image002

5. This would equate to two features the list instance feature and the pages feature

a. The pages module would be the NewForm, DisplayFormand EditForm forms.

6. On the last list instance of the Project, I figured out that in reality we didn’t need the Pages module at all! We just needed to change a couple of lines in the schema.xml file. (Who knew! As that file is a monstrosity!) I am making the assumption that you would need this module if you had some customization of the NewForm, DisplayForm and EditForm pages.

a. Here is all that needed to be changed in the schema.xml:

From:

<Forms />

To:

<Forms>

<Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

</Forms>

7. I have since found an even sweeter way to do this whole process that is repeatable and will make our lives as developers much easier!

8. I found and Visual Studio Extension project on CodeProject that makes the third step in creating list instances trivial!

http://www.codeproject.com/KB/sharepoint/ImportList.aspx

Kudo’s to: Mark Nischalke

9. So the steps are:

a. Create the Content Type and Site Columns in Code (push to the site)

b. Create the List in the UI (adding Content type and changing the default view).

c. Import the List Instance into the project with the List Import Command on the Add new Item menu.


Saturday, August 20, 2011 #

I had added some code behind to a SharePoint 2010 master page and it worked beautifully! I followed Andrew Connell’s blog and article on MSDN. I had actually created two more master pages with an identical code behind as they each needed the same bits. I had each inherit from the same code behind file. Everything worked great until we were having some problems with one of our developers opening a css file form the VPN. So I thought I would just change the master page in SPD temporarily to use a css file from a different folder which he had permissions to. Easy Huh! I did that and lo and behold my site crashed with an error the said: My Master page was not registered as safe! I did some digging and found some references on MSDN discussing pages: MSDN BLOG which discussed safe control entries and Page Parser Paths. I saw that this was an identical problem as I was having with the Master Page. It works until it is edited, even as simply as a user editing with SharePoint designer. To fix the issue, I followed the steps:

1. Added a safe control entry on the Master Page Module

<SafeControl Name="SafeControlEntry2" Assembly="$SharePoint.Project.AssemblyFullName$" Namespace="Your Master Page NameSpace" TypeName="*" IsSafe="true" IsSafeAgainstScript="false" />

This can be done by setting the safe control entry by highlighting the module ad looking at the properties window see (MSDN BLOG):

clip_image002[6]

clip_image004[5]

2. I added a web Configuration Feature Receiver which added my master page(s) PageParserPath in the web configuration file:

public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

SPWebApplication webApplication = properties.Feature.Parent as SPWebApplication;

SPSecurity.RunWithElevatedPrivileges(delegate()

{

SPWebConfigModification mod = new SPWebConfigModification();

mod.Path = "configuration/SharePoint/SafeMode/PageParserPaths";

mod.Name = "PageParserPath[@VirtualPath='/_catalogs/masterpage/1.master']";

mod.Owner = "NameMaster";

mod.Sequence = 0;

mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

mod.Value = "<PageParserPath VirtualPath='/_catalogs/masterpage/1.master’ CompilationMode='Always' AllowServerSideScript='true' />";

webApplication.WebConfigModifications.Add(mod);

SPWebConfigModification mod2 = new SPWebConfigModification();

mod2.Path = "configuration/SharePoint/SafeMode/PageParserPaths";

mod2.Name = "PageParserPath[@VirtualPath='/_catalogs/masterpage/2.master']";

mod2.Owner = "NameMaster";

mod2.Sequence = 0;

mod2.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

mod2.Value = "<PageParserPath VirtualPath='/_catalogs/masterpage/2.master' CompilationMode='Always' AllowServerSideScript='true' />";

webApplication.WebConfigModifications.Add(mod2);

SPWebConfigModification mod3 = new SPWebConfigModification();

mod3.Path = "configuration/SharePoint/SafeMode/PageParserPaths";

mod3.Name = "PageParserPath[@VirtualPath='/_catalogs/masterpage/3.master']";

mod3.Owner = "NameMaster";

mod3.Sequence = 0;

mod3.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

mod3.Value = "<PageParserPath VirtualPath='/_catalogs/masterpage/3.master' CompilationMode='Always' AllowServerSideScript='true' />";

webApplication.WebConfigModifications.Add(mod3);

WebApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

webApplication.Update();

});

}

// Uncomment the method below to handle the event raised before a feature is deactivated.

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{

SPWebApplication webApplication = properties.Feature.Parent as SPWebApplication;

SPSecurity.RunWithElevatedPrivileges(delegate()

{

Collection<SPWebConfigModification> mods = webApplication.WebConfigModifications;

int initialModificationsCount = mods.Count;

for (int i = initialModificationsCount - 1; i >= 0; i--)

{

if (mods[i].Owner == "NameMaster")

{

SPWebConfigModification modToRemove = mods[i];

mods.Remove(modToRemove);

}

}

if (initialModificationsCount > mods.Count)

{

webApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

webApplication.Update();

}

});

}

No, I don’t name my master pages 1.master, 2.master, and 3.master! I just removed the actual names to protect my client.

The result of these changes in the web.config are:

1. PageParserPaths’s

<SafeMode MaxControls="200" CallStack="false" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false">

<PageParserPaths>

<PageParserPath VirtualPath="/_catalogs/masterpage/1.master" CompilationMode="Always" AllowServerSideScript="true" />

<PageParserPath VirtualPath="/_catalogs/masterpage/2.master" CompilationMode="Always" AllowServerSideScript="true" />

<PageParserPath VirtualPath="/_catalogs/masterpage/3.master" CompilationMode="Always" AllowServerSideScript="true" />

</PageParserPaths>

</SafeMode>

2. SafeControl

<SafeControl Assembly="NameSpace.MasterPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2d6526e879238f35" Namespace="NameSpace.MasterPages" TypeName="*" Safe="True" SafeAgainstScript="False" />

I checked and was able to edit the page in SPD and fixed the issue of crashing the site! SharePoint is Tribal in many areas, the platform is so vast there just isn’t anywhere that’s has all the answers!

 

 

 

 

 


Sunday, August 14, 2011 #

I have a client that desired to have the MOSS 2007 capability of My Links but in no way wanted My Sites in SharePoint 2010.  I took some steps that gave them the capability of My Links by creating a personalized web part that could be linked to in a modal.  It worked, However, in addition, they didn’t want users without web admin rights to see the SharePoint ribbon. The result was that admins could use the web part and the personalization data would be kept by SQL in the personalization database.  Members could not edit the web part as they didn’t have access to the Ribbon.  (Catch 22).    I took another look at the MOSS 2007 My Links and SharePoint 2010 My Links and I saw that the Personalization was to tied to the User Profile Service and was not tied to SharePoint My Sites!  Ah Ha!, we could deliver this and SharePoint would do all the work!  Here are the steps to make this happen:

1.  Go to the web part gallery:

image

2.  Click the Documents Tab, and then New Document:

image

3.  A Modal will open up, scroll to Microsoft.SharePoint.Portal.Web Controls.QuickLinksMicroView

image

4.  Scroll back up to the top of the page and click on the Populate Gallery button:

image

5.  Create a page in the pages library at the site collection level and use the Blank Web Parts page layout.  Add the QuickLinksMicroView web part to the header web part zone:

image

6:  Edit the web part properties and change display to Title Only, and The name to My Links;

image

7:  Save the page and Publish.

 

I wanted this page to appear in a modal from the master page so I create a link in the global links portion of the master page.  I used an ASP.NET Hyperlink Control as I needed to set the value of the link in the master page code behind.  I didn’t at first and used a simple anchor tag with href .  However, I couldn’t think of any way that would allow the correct link to be passed to a JavaScript from sub pages and possibly the layouts folder, it worked just fine at the site collection level but not in sub sites.  Therefore, the reason for the code behind.

 

The code behind on the master page:

 


protected void Page_Load(object sender, EventArgs e)
{

    if (!IsPostBack)
    {

        SPSite site = SPContext.Current.Site;

       
        HyperLink hl = this.FindControl("HyperLink4") as HyperLink;
        hl.NavigateUrl = String.Format("javascript:OpenDialog('{0}/pages/links.aspx')", site.Url);

    }
}

 

where open Dialog is a JavaScript function load in the master page Script tag:

 

function OpenDialog(myurl) {
    var options = SP.UI.$create_DialogOptions();
    options.url = myurl;
    // options.width = 500;       
    // options.height = 400;      
    //options.dialogReturnValueCallback = Function.createDelegate(null, CloseCallback);        
    SP.UI.ModalDialog.showModalDialog(options);
}

 

The demo I  am making here does not have this master page so I will just open the MyLinks page with different users to show how it works. 

image

8:  Click on the My Links (link) this will open up the the My Site Host which is linked to the Users Profile:

image

 

image

image

9:  Now Log in as a different user:

image

The Rest is the same as above.  Personalization is taking place.

A great way to get users to their profile page and add custom links which will follow them around while in the site collection.  The master page I am demo’ing here has the I Like it and Tagging control in the upper right so it will also get users to see their Tags and Notes from their profile page.  For example:  I tag the Generic Landing Page:

 

image

 

image

 

The client Didn’t want my sites so I created another master page for the My Site Host and hid the Links for MY Site,  and My Content and made sure that the client turned off self service site collections.  In addition I had the Site Admin add a link in Central Admin to add a Portal Link back to the site collection root on all Profile pages.


Tuesday, January 18, 2011 #

I have been working on a Silverlight Navigation application which uses LINQ to query an XML data source and has been working without a hitch until I added a control  to the landing page. All other pages which worked fine were loaded from the navigation service    Once I added the control I got a page not found error!  (The page is there) and If navigated to, it works as expected.   What happens, it that I closed the error Modal dialog and then clicked on another page, then navigate back to the landing page it works!  However, the page doesn’t load on initialization. 

I thought the issue is with the data access timing.  Because the data access (LINQ query) is asyncronomous it hasn't finished retrieving data before the control on the page is requesting the data.  If I single step through the code I see a The typical object error when trying to access the collection being filled from the data access code.

On my Development Computer, I do not have an issue as I have a dummy data source to query against and the Page loads fine!

So the issue seems to be that I need to put a delay of some sort before the controls are loaded. Basically the entire application loaded some data from an xml file gathered from a SharePoint list.

I found the solution by re-querying the data source and after the data loaded, I added my control to the page controls collection dynamically.

This was my first ever issue with asyncronomous programming and it is a lesson which will be ingrained into my Silverlight development forever as I had a few hours of debugging to help chisel it into my brain housing group!


Friday, October 01, 2010 #

 

Lets put together a Ribbon Project which demonstrates my last post.

Create an empty SharePoint Project, here named testRibbonCommunications:

 image

Add New Item

image

 

  1.  
  2.  
  3.  
  4.  

 

 

 

 

Choose Application Page (I named this TestRibbon.aspx)

 

image

 

image

Add the following code in a PageComponent.js file to your layouts folder and change the namespace to fit your project (testRibbonCommunications) just do a find / replace:

/// <reference name="MicrosoftAjax.js" />
/// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.core.debug.js" /> 
/// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.debug.js" />  

function ULS_SP() {
  if (ULS_SP.caller) {
    ULS_SP.caller.ULSTeamName = "Windows SharePoint Services 4";
    ULS_SP.caller.ULSFileName = "/_layouts/testRibbonCommunications/PageComponent.js";
  }
}


Type.registerNamespace('testRibbonCommunications');

// RibbonApp Page Component
CaseSmartIntakeForm.PageComponent = function () {
  ULS_SP();
  CaseSmartIntakeForm.PageComponent.initializeBase(this);
}


CaseSmartIntakeForm.PageComponent.initialize = function () {
  ULS_SP();
  ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, testRibbonCommunications.PageComponent.initializePageComponent), 'SP.Ribbon.js');
}


testRibbonCommunications.PageComponent.initializePageComponent = function () {
  ULS_SP();
  var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
  if (null !== ribbonPageManager) {
    ribbonPageManager.addPageComponent(testRibbonCommunications.PageComponent.instance);
    ribbonPageManager.get_focusManager().requestFocusForComponent(testRibbonCommunications.PageComponent.instance);
  }
}


testRibbonCommunications.PageComponent.refreshRibbonStatus = function () {
  SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);
}


testRibbonCommunications.PageComponent.prototype = {
  getFocusedCommands: function () {
    ULS_SP();
    return [];
  },
  getGlobalCommands: function () {
    ULS_SP();
    return getGlobalCommands();
  },
  isFocusable: function () {
    ULS_SP();
   return true;
  },
  receiveFocus: function () {
    ULS_SP();
    return true;
  },
  yieldFocus: function () {
    ULS_SP();
    return true;
  },
  canHandleCommand: function (commandId) {
    ULS_SP();
    return commandEnabled(commandId);
  },
  handleCommand: function (commandId, properties, sequence) {
      ULS_SP();

    return handleCommand(commandId, properties, sequence);
  }
}


// Register classes
testRibbonCommunications.PageComponent.registerClass('testRibbonCommunications.PageComponent', CUI.Page.PageComponent);
testRibbonCommunications.PageComponent.instance = new testRibbonCommunications.PageComponent();


// Notify waiting jobs
NotifyScriptLoadedAndExecuteWaitingJobs("/_layouts/testRibbonCommunications/PageComponent.js");

 

 

 

 

 

 

 

 

 

 

 

 

 

In the code behind for your application page add the  ICallbackEventHandler Interface to the page code behind:

 

 

namespace testRibbonCommunications.Layouts.testRibbonCommunications
{
    public partial class TestRibbon : LayoutsPageBase, ICallbackEventHandler
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }


        public string GetCallbackResult()
        {
            throw new NotImplementedException();
        }

        public void RaiseCallbackEvent(string eventArgument)
        {
            throw new NotImplementedException();
        }
    }
}

The XML is terse but once you have done one tab and button you can do them all.

Add the following region to the code behind of the application page (A tab with a single button):

 

        #region tabxml
        private string mainTab = @"
    <Tab
        Id=""Ribbon.MyTab""
        Title=""Daves Tab""
        Description=""Daves Tab""
        Sequence=""1105"">

      <Scaling
        Id=""Ribbon.MyTab.Scaling"">
        <MaxSize
        Id=""Ribbon.MyTab.MaxSize""
        GroupId=""Ribbon.MyTab.MyGrp""
        Size=""OneLargeTwoMedium""/>
        <Scale
        Id=""Ribbon.MyTab.Scaling.CustomTabScaling""
        GroupId=""Ribbon.MyTab.MyGrp""
        Size=""OneLargeTwoMedium"" />
      
    </Scaling>
    <Groups Id=""Ribbon.MyTab.Groups"">

        <Group
        Id=""Ribbon.MyTab.MyGrp""
        Description=""Actions""
        Title=""Status""
        Sequence=""52""
        Template=""Ribbon.Templates.MyGrp"">
        <Controls>
          


            <Button
            Id=""Ribbon.MyTab.MyGrp.Save""
            Sequence=""56""
             Image32by32=""/_layouts/1033/images/formatmap32x32.png""
            Image32by32Top=""-416""
            Image32by32Left=""-256""
            Description=""Save Button""
            Command=""Ribbon.MyTab.MyGrp.Save.Click""
            LabelText=""""
            TemplateAlias=""cust2""/>


        </Controls>

        </Group>
      

    </Groups>
    </Tab>";

        private string contextualTabTemplate = @"
    <GroupTemplate Id=""Ribbon.Templates.MyGrp"">
    <Layout
        Title=""OneLargeTwoMedium"" LayoutTitle=""OneLargeTwoMedium"">
        <Section Alignment=""Top"" Type=""OneRow"">
        <Row>

 
            <ControlRef DisplayMode=""Large"" TemplateAlias=""cust2"" />
    
  
        </Row>
        </Section>

    </Layout>

    </GroupTemplate>";

        #endregion

 

Add the following region to the code behind of the application page:

 #region tabMethods
        private void AddRibbonTab()
        {
            // Gets the current instance of the ribbon on the page.
            SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);

            //Prepares an XmlDocument object used to load the ribbon
            XmlDocument ribbonExtensions = new XmlDocument();

            //Load the contextual tab XML and register the ribbon.
            ribbonExtensions.LoadXml(this.mainTab);
            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild,
                "Ribbon.Tabs._children");

            //Load the custom templates and register the ribbon.
            ribbonExtensions.LoadXml(this.contextualTabTemplate);
            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild,
                "Ribbon.Templates._children");



            ribbon.Minimized = false;
            ribbon.CommandUIVisible = true;
            const string initialTabId = "Ribbon.MyTab";
            if (!ribbon.IsTabAvailable(initialTabId))
                ribbon.MakeTabAvailable(initialTabId);
            ribbon.InitialTabId = initialTabId;
        }



        private void AddTabEvents()
        {
            var commands = new List<IRibbonCommand>();

            // register the command at the ribbon. Include the callback to the server     // to generate the xml

            commands.Add(new SPRibbonCommand("Ribbon.MyTab.MyFormGrp.Save.Click", "setSave();", "true"));

        

            //Register initialize function
            var manager = new SPRibbonScriptManager();
            var methodInfo = typeof(SPRibbonScriptManager).GetMethod(
            "RegisterInitializeFunction",
            BindingFlags.Instance | BindingFlags.NonPublic);
            methodInfo.Invoke(manager, new object[] { 
        Page, "InitPageComponent", 
        "/_layouts/testRibbonCommunications/PageComponent.js", false, 
        "testRibbonCommunications.PageComponent.initialize()" });

            // Register ribbon scripts
            manager.RegisterGetCommandsFunction(Page, "getGlobalCommands", commands);
            manager.RegisterCommandEnabledFunction(Page, "commandEnabled", commands);
            manager.RegisterHandleCommandFunction(Page, "handleCommand", commands);
        }
        #endregion


 

 

 

Add the following region to the code behind of the application page:

#region pageEvents

protected void Page_Load(object sender, EventArgs e)
{


    if (!IsPostBack)
    {



    }
}

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    this.AddRibbonTab();

    AddTabEvents();



    String cbReference = Page.ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context");

    String callbackScript;

    callbackScript = "function CallServer(arg, context)" +

    "{ " + cbReference + ";}";

    Page.ClientScript.RegisterClientScriptBlock(this.GetType(),

    "CallServer", callbackScript, true);

}

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

}
#endregion

 

The above code blocks tie the ribbon pieces together

 

Add a JavaScript Page to the solution in the Layouts folder (PageScript.js):

 

function setSave() {
    //call server
    SP.UI.Notify.addNotification("You pressed Save, ", false);
    CallServer("Save", "");

}


// variable to hold the server data
var itemsXml;

// This function will receive the callback from the server with the data. 
function ReceiveServerData(arg, context) {
    itemsXml = arg;
}

Link the JavaScript file to the page by adding this to the page AdditionalPageHead section: use your page URL

 

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    <SharePoint:ScriptLink Name="sp.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink1" />
    <SharePoint:ScriptLink Name="CUI.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink2" />
    <SharePoint:ScriptLink Name="/_layouts/testRibbonCommunications/PageComponent.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink3" />
    <SharePoint:ScriptLink Name="/_layouts/testRibbonCommunications/pageScript.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink4" />
   
 
</asp:Content>

 

 

Replace the ICallbackEventHandler methods with this region:

#region ICallbackEventHandler Members

      public string GetCallbackResult()
      {

          //I am returning nothing
          string returnData = "Call Back Results";
          return returnData;
      }

      public void RaiseCallbackEvent(string eventArgument)
      {


          if (eventArgument.Contains("Save"))
          {
              //handle the save event here

          }


      }
      #endregion

 

Deploy the solution and browse to the page:

 

image

Everything should be good to go!

 

 

Set a Breakpoint at the RaiseCallbackEvent(string eventArgument) and hit F5 Browse to the layouts page and click the save button, Notice the breakpoint get hit and enters the empty test.

image

There you have it!  Here is the solution for you to build on! TestRibbonCommunications


Thursday, September 30, 2010 #

My last project had me do some SharePoint 2010 Ribbon customizations.  I though I would blog about some of the things I learned!  There are many ways to create a Ribbon component. You could use a custom action to a tab to an existing Ribbon or in the case that I will be discussing/demonstrating  is creating a complete new tab for a SharePoint Application page.   The steps developer should take :

  1. Work through the Hand-On Lab provided by Microsoft on Customizing the Ribbon. This is part of The SharePoint Developer Training Kit which can be downloaded here: Developer Training Kit
  2. Read  Chris O'Brien blog series on customizing the Ribbon!  Really Good!
  3. I found this gem by accident! Download this Visual Studio Add In for Customizing the Ribbon:  SharePoint Ribbon Extensibility

A SharePoint Ribbon is non trivial there is a lot of work involved in just getting the ribbon and tabs to appear:

  1. The XML to get the tab/controls on the Ribbon
  2. Getting the Page Component set up
  3. Linking to custom JavaScript
  4. Communicating to the server/client from the Ribbon

There are plenty of blogs about the ribbon and they are very good.  This post is not a rehash on how to create a Ribbon but on Client/Server communication via the Ribbon.

Client Server communications is the big one as the SharePoint 2010 Ribbon is mostly controlled via JavaScript.  How to get the JavaScript methods to invoke server side code.  I saw many blog posts which describe how to dynamically populate a drop  down or fly out through a delegate, etc.  However, they were related to this single instance and didn’t seem to be extendable to a general way to handle all the buttons in a Ribbon.  The first thing I looked at was using was the  IPostBackEventHandler Interface and I was successful in getting the ribbon button commands to call the server!  However, a big gotcha was that they required a post back!  In the modern web 2.0 era this is a big no no!  I wound up using the ICallbackEventHandler Interface and this was the ticket!  The application could respond to button clicks via JavaScript and call the server without Post Back!!!!  I might add that the gist of the code required came from this blog post: Patrick Boom.  Microsoft has a great article on how too use this Interface here: Call Back Event handler  The interesting part of this is how the JavaScript call back is invoked:

 

ClientScriptManager cm = Page.ClientScript; 
String cbReference = cm.GetCallbackEventReference(this, "arg", "ReceiveServerData", ""); 
String callbackScript = "function CallServer(arg, context) {" + cbReference + "; }"; 
cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);

notice the CallServer method is built dynamically and has arg as a parameter this is what we need to make a general purpose method to get our client click buttons from JavaScript.  The ICallbackEventHandler Interface requires two methods on the server:

 

public void RaiseCallbackEvent(String eventArgument) { }

and 

public string GetCallbackResult() { return aStringValue; }

 

The one I am interested for this discussion is RaiseCallbackEvent it has eventArgument as a parameter.  We can link these two items together via arg and eventArgument by using a JavaScript call on our Ribbon  buttons.  A Ribbon command might look like this for Save.  Where the JavaScript Method is setSave().

 

            commands.Add(new SPRibbonCommand("Ribbon.MyTab.MyFormGrp.Save.Click",  "setSave();", "true"));


The setSave()  JavaScript method:

 

function setSave() {
    //call server
    SP.UI.Notify.addNotification("You pressed Save, ", false);
    CallServer("Save", "");

}

Here is the major point:  I can thank my friend  William.Schindler@neudesic.com for helping me see this.  Notice the CallServer(“Save”,””) this is how we can distinguish which button has been clicked on the Server Ribbon. Thus on the Server Side in RaiseCallbackEvent method we might have something like this:

 


public void RaiseCallbackEvent(string eventArgument)
    {

        if (eventArgument.Contains("Save"))
        {
            //handle the save event here

        }
    }

You can handle any Ribbon Button click by hooking here!

There is a piece missing here that I haven’t covered and that is the client callback!  For this discussion I am not using it.  I only wanted to be able to get server side processing on client click events for the Ribbon.  I am not new to coding and I am sure there are other ways to solve this puzzle but this one offers a repeatable way to hook on the server side to the Ribbon Events!


Wednesday, August 18, 2010 #

I am blogging this as I hope to prevent anyone else spending extra time on this.  I was having problems with a content type/site columns feature.  Actually everything was working correctly except choice fields were not displaying:

 

I should have been guided by the fact that I could do it in a Console application but not in a feature!  However, I am getting a little older and my mind is still “Tarp as a Shack”  you get it!    The problem is that in Sandboxed solutions intellisense is not pared to the subset of code that is allowed!  I think this particular code  issue is a bug and needs to be addressed by Microsoft as it has a far-reaching effect of having code that should be Sandboxed run as a farm solution. In any event here is how I troubleshot this particular issue:

clip_image002

Lookup Field working

clip_image004

Choice Fields not working

clip_image006

Multi Choice Field is disabled???

Debugging when these are added shows that the field is not being updated:

clip_image008

clip_image010

Shows count = 0 on choices????? Hmmm; this can only mean one thing!  We cannot change the choices in a sandboxed solution!

Yes, This is a bug!!!

clip_image012

I added the code as a farm solution and these columns work!!! 

Okay!  Why am I blogging this?  To do sandboxed code without a full trust proxy there are many decisions that are made to make your code work!  For example in this project one of the decisions I made was that I didn’t have access to the site property bag  from a sandboxed solution so I had to resort to adding a new list for Configuration as Microsoft’s new guidance around content types is not to delete them but hide them as they may be referenced from other sources.  So, I added the list and used it to see if the content type exists, if it does: Hide the content type and add a new one with a different name on the feature activation event. Then write the  new name back to the configuration list.  Another, was I couldn’t use logging .  Event  logging is not allowed in a sandboxed solution.

To further address this is that Microsoft has published  SharePoint 2010 Guidance solution  which does have a logging and Property bag ( PROXY) which mitigates these two issues.  So maybe they should be on every SharePoint 2010 farm. I have spent an inordinate amount of time trying to solve issues around adding content types in code from within a sandboxed solution .  From what I see here is that it is possible for a subset of the fields that might be required but certainly not Choice fields.   So the end result is that I could have made this a farm solution to begin with.  The solution has three projects content type (Farm Solution) drop down lookup lists (Sandbox Solution) and configuration list (Sandbox Solution).  I hope this helps others  not make my mistake in the future.


Sunday, August 01, 2010 #

   I have spent some time, around all 6 of the SharePoint 2010 data access technologies

 

1.  Using SharePoint standard web services

2.  SharePoint Client Object Model

3.  Silverlight SharePoint Client Object Model

4.  Data Services Client Model

5.  OWSRV.DLL

6.  SharePoint Object Model

 

Silverlight is a client application so the data access technologies applicable are:

1. SharePoint web Services

2. Silverlight Client Object Model

3. Data Services Client Model

4. OWSSRV.DLL

These 4 have many features in common most of which centers around all queries need to be  asynchronous  so the queries must have delegates for begin and end. 

In Comparing and Contrasting them against each other.  Much of the technology for Data Services, Silverlight Client Object Model, and SharePoint Client Object Model is new and the documentation is incomplete.   I like the Data Services technology as this will be a common pattern across all of the Microsoft stack.  In addition it seems easier to put together an application with it.

 

The common LINQ to SharePoint scenario is to use SPMetal on the current site and use the strong typing of SharePoint Lists which requires some work from the developer to put together.  In contrast, adding a service reference to http://<<your site>>/{vti_bin/listsdata.svc will automatically add a strongly typed class for all lists in the development site.  Much of the hype about data services centers around the URL syntax of the queries available and the output xml which is much like the way SharePoint data was consumed via web services or OWSRV.DLL: read the xml into an xml document and then parse the result.  In reality, for a real application, that happens behind the scenes. The developer just needs to access data using LINQ to SharePoint as with using the SharePoint Client Object Model or Silverlight Client Object Model.

 

I will use this technology with Silverlight and SharePoint 2010,  In a previous blog I showed you how easy to integrate Silverlight and a SharePoint project together  using Visual Studio 2010.  SharePoint 2010, Silverlight and Visual Studio

 

I will use the same demo project I used with the previous blog and add some code accessing list data using data services.

 

1.  Add a Service Reference to the project

2

 

1

 

 

 

2,  Choose Show Data Sources from the toolbar. See all the lists in the site have a class like spmetal which  have  strongly typed classes.

3

3.  I am going to choose a list with a lot of data to add to Silverlight project, I’ll just drag it onto the Silverlight screen and get a Silverlight Data Grid and edit the columns for viewing/demonstration purposes.: MasterPageGallery

editcolumns

datagrid

 

4.  Basically we have the shell to display the data; However now we need to get the list data and then display it.  When, the datagrid is added to the Silverlight User Control it adds some demo code to the silverlight user control loaded event which guides us in how and where to load the data:

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {

            // Do not load your data at design time.
            // if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            // {
            //  //Load your data here and assign the result to the CollectionViewSource.
            //  System.Windows.Data.CollectionViewSource myCollectionViewSource = (System.Windows.Data.CollectionViewSource)this.Resources["Resource Key for CollectionViewSource"];
            //  myCollectionViewSource.Source = your data
            // }
        }
5.Add a class to make handling the data easier. This class is just a couple of properties which make data access easier (Thanks MSDN):
datacontextclass 
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DemoSilverlightApplication.ListDataService;
using System.Data.Services.Client;
namespace DemoSilverlightApplication
{
    public class DataServicesContext
    {
        HomeDataContext _ctx;
        //used by Data Services got a strongly typed Observeable Collection
        DataServiceCollection<MasterPageGalleryItem> _masterPageGallery;
        public HomeDataContext DataContext
        {

            get
            {
                if (_ctx == null)
                {
                    _ctx = new HomeDataContext(
                        new Uri("http://djacobus01/_vti_bin/listdata.svc", UriKind.Absolute
                        )
                     );
                }
        
                return _ctx;
            }
        }

        public DataServiceCollection<MasterPageGalleryItem> MasterPageGalleryDataServiceColl
        {
            get
            {
                if (_masterPageGallery == null)
                    _masterPageGallery = new DataServiceCollection<MasterPageGalleryItem>(DataContext);
                return _masterPageGallery;


            }
        }
    }
}

Where most of the class can be gleaned from intellisense.

 

6.  Add a data context to the MainPage.xaml.cs:

 

        private DemoSilverlightApplication.DataServicesContext _currentContext;
        public DemoSilverlightApplication.DataServicesContext CurrentContext
        {
            get
            {
                if (_currentContext == null)
                    _currentContext = new DemoSilverlightApplication.DataServicesContext();
                return _currentContext;
            }
        }

 

contextcode

 

6.  We need to add a method to get all the master pages:

 

 public void GetAllMasterPageItems()
        {

            var query = (
                   from Item in CurrentContext.DataContext.MasterPageGallery
                   where Item.Id > 0
                   select Item
            ) as DataServiceQuery<MasterPageGalleryItem>;
            query.BeginExecute(
                (IAsyncResult asyncResult) => Dispatcher.BeginInvoke(() =>
                {
                    DataServiceQuery<MasterPageGalleryItem> queryResults = asyncResult.AsyncState as DataServiceQuery<MasterPageGalleryItem>;
                    if (queryResults != null)
                    {
                        CurrentContext.MasterPageGalleryDataServiceColl.Clear(true);

                        CurrentContext.MasterPageGalleryDataServiceColl.Load(
                             queryResults.EndExecute(asyncResult)
                        );
                    }
                })
                , query
             );

            myData = CurrentContext.MasterPageGalleryDataServiceColl;
        }

7.  The above method is the correct way to retrieve list data using Data Services.  Therefore using this project as a template you can start using this Data Access Technology.

8.  Here is the page with the Silverlight web part

 

page

 

9.  Here is a link to the project: Demo project   Since all SharePoint 2010 site collections have a master page gallery the source code should work work with an update to the service reference, Update the data context class with the url to your server and update the SharePoint Project with the URL of your server:

 

 

updatecontext

spproject


 

I started training on SharePoint 2010 about 3 months ago and I decided that I needed to blog about the ease of developing Silverlight applications with Visual Studio 2010 for SharePoint 2010. In Visual Studio 2008 the methodology would be to:

1. Create a Silverlight application

2. Create the XAP file

3. Move the XAP file into a SharePoint document library or Layouts folder

4. Put a Silverlight Web part on a SharePoint page (not OOTB)

a. Link the Silverlight web part to the XAP file

5. Debug the web part by “Attaching to Process”

6. When the XAP File changed:

a. Copy it back to the document library or Layout folder

Using Visual Studio 2010:

1. Create a SharePoint Blank application

2. Add a Silverlight Web Application

3. Add a module to the SharePoint

a. Add the output of the Silverlight Project to the module

4. Put a Silverlight Web part on a SharePoint page (on my dev. box I set the home page, OOTB)

a. Link the Silverlight web part to the XAP file

5. Hit the F5 key and you are up and debugging

The important distinction here is that you are up and debugging with one keystroke.

I will walk you through the process of setting up a SharePoint 2010/Silverlight project

1. Create a blank SharePoint project in 2010:

1

2. Set it as a SandBoxed solution if applicable

2

3. Add a Silverlight application

3

4. Add a module to the SharePoint application

4

4a

5. Link the output of the Silverlight Project to the SharePoint Module By clicking the module:

5

6. In the properties window click the ellipse’s next to Project Output references

6

7. In the window that appears click add

7

8. In the Properties pane Click Deployment Type and choose Element File

8

9. In the Properties pane Click Project Name and choose the output project to be the Silverlight Web Project (not the application test web site)

9

10. Immediately the module will get the Silverlight XAP file added to it!

10

11. Delete the file reference to the sample.txt file from the module

11

12. Add a URL to the module in this case documents

12

13. Add some content to the Silverlight Project ( I will Add Hello World)

13

13a

14. Configure the web part on the home page/Development site

14

15. Choose the SharePoint Project as the startup project and hit F5. If the web part is on the home page it will come up debugging else browse to the page and “click deploy” on the Build Menu and then refresh the page. Here is the page with Hello World:

15

16. Adding some new text to debug:

16

17. Here is the page being debugged (not using F5) as I don’t have the test web part on the home page. I just attach to process: Using Silverlight as the key to which process to debug:

17

18. Hitting the Breakpoint:

18

19. The new text:

19


Monday, February 23, 2009 #

I have been working with Silverlight 2 and its integration into SharePoint and I am a convert!  I had thought that this was an application that would change the face of the internet!  I just kind of browsed it while it was in its 1.0 stage and now I am a confirmed Silverlight advocate!  I had a web part that I built that was an ad rotator with lost of business process on the server.  You know the kind of rules:

1.  GoLiveDate

2.  ExpireDate

3.  SortOrder

4.  LinkURL

5. Approved!

6.  More!

Not hard to do on the server but then prior to Ajax some kind of javascript need to be invoked which ran the automatic ad rotator!  A lot of work and coorination between server side code and javascript!  In Silverlight this is a trivial operation all in C# with a CAML Query!  I wish I could go an update all that cludgy code I created in the past without Silverlight!  A web part that took days before cab be built in a few hours!  In my next post, I will show how to build this ad rotator for a SharePoint List .

 


I hadn't looked for this update as 1.2 was pretty fresh!  But I came upon it by accident!  Wow!  They are finally getting it!  Just the ability to create the WSP in a resusable fasion is fantastic!  But the additional capability of a blank web site definition that can be a publishing site is the greatest!  It appears as if some of WSP Builder is baked in!  I had a Silverlight Host Control built by the Microsoft team with 1.2 but was thinking I would have to build a WSP by hand using WSP Builder when this came up in my browser!  It was a right click!  Wow!  I needed this!!!!!!!!!!!!!!!

Tuesday, October 28, 2008 #

A blog that provides details but not a solution which compiles and installs is not very helpful as there is just too much going on and assumptions about the knowledge of the reader are impossible to make! I have tried to follow many step by step tutorials and had the final product fail because of missing steps or my understanding of the steps!

If you add this solution, you must activate the feature stapler at the site collection level prior to creating a new site.  If you create a new site collection with this solution this step is unnecessary.  The solution provided is not exactly the same as described in the blog entries Part 2 - part 4.  But is close enough to validate the methodology.  I have had a few tell me that I could have added the web parts and lists in the onet.xml file:  I know!  But I have had problems with this file in the past and have decided that using code to add the web parts and lists as features is better and in my opinion easier than with xml in onet.xml.  Code gives me access to intellesence and I feel comfortable in code and in terror of all the xml that can crash an installation!!!!

The code for this series of articles can be downloaded here:

ABCSite DownLoad


Monday, October 27, 2008 #

ONET.XML

Part 4 is the real difference in the site definition we are creating and those offered in many books and blogs. Except for the methodology I am presenting, I haven’t really offered anything new! Today we will start to deviate. The ONET.xml file is fairly simple when you get to using it a few times! The ONET.XML file I am proposing is very simple and not much in it except for some OOTB xml and the only thing that will change in this instance is adding all the features we have created and two more new features! Feature receiver for code and a feature stapler to add the code to the site definition. But first let’s complete the ONET.XML file:

1. NavBars Element: We will keep the default quick launch so we will leave it alone!

clip_image002

2. List Templates Element is empty we’ll leave the same

3. Document Templates we will leave as is

4. Configurations elements: We’ll only have one so we will delete all but the default configuration.

a. We’ll keep <Configuration ID="0" Name="Default">

b. We’ll delete all the List Elements

i. We are using features to provision the lists

ii. The only configuration elements that are not empty is: Site Features and Web features

iii. WOW! This is in concert with the whole idea around the feature solution framework!

5. Modules elements: We will delete all the modules.

clip_image004

6. We will now add the features we have created to this point in the WSPBuilder project

clip_image006

7. We are done with ONET.XML (Easy Huh!)

a. But wait we haven’t provisioned default.aspx

i. It is normally provisioned via a module in ONET.XML

ii. We will provision it in a new feature!

1. So we can add a feature receiver

2. Add a new feature folder ABCTeamSiteRec

a. Build the site Definition as we need the strong name

b. Use Red Gate’s reflector to get the needed information

c. Drop the DLL into Red Gate and get the needed information

clip_image008

8. Add the following in the feature XML file:

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

<Feature

Id="{9F2EA305-7F72-40fd-A0E8-5353A5CBB251}"

Title="A Company feature: SiteDefinition Activation"

Description="SiteDefinition Activation"

Version="1.0.0.0"

Scope="Web"

Hidden="TRUE"

ImageUrl="ABCteamSite/company10.jpg"

ReceiverAssembly="ABCSiteDef, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e540898f86a0353c"

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

<ElementManifests>

<ElementManifest Location="elements.xml"/>

</ElementManifests>

</Feature>

9. Copy the default.aspx page to this feature folder and then add the following into the elements. Xml file:

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

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

<Module Name="HomePage" Path="" Url="">

<File

Url="default.aspx"

Type="Ghostable"

IgnoreIfAlreadyExists="False"/>

</Module>

</Elements>

10. Next we need to add the feature receiver add the following into a class at the root of the site not in the 12 hive

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;

namespace ABCSiteDef

{

public class FeatureReceiver : SPFeatureReceiver

{

public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

SPWeb currentWeb = null;

SPSite currentSite = null;

object oParent = properties.Feature.Parent;

if (properties.Feature.Parent is SPWeb)

{

currentWeb = (SPWeb)oParent;

currentSite = currentWeb.Site;

}

else

{

currentSite = (SPSite)oParent;

currentWeb = currentSite.RootWeb;

}

// Get the web part manager

SPLimitedWebPartManager mgr = currentWeb.GetLimitedWebPartManager("default.aspx", System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

// Tasks List

SPList list = currentWeb.Lists["Tasks"];

// Instantiate the web part

ListViewWebPart wp = new ListViewWebPart();

wp.ZoneID = "Left";

wp.ListName = list.ID.ToString("B").ToUpper();

wp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();

mgr.AddWebPart(wp, "left", 1);

mgr.AddWebPart(wp, "left", 1);

// Calender List

SPList list7 = currentWeb.Lists["Calendar"];

// Instantiate the web part

ListViewWebPart wp7 = new ListViewWebPart();

wp7.ZoneID = "Left";

wp7.ListName = list7.ID.ToString("B").ToUpper();

wp7.ViewGuid = list7.DefaultView.ID.ToString("B").ToUpper();

mgr.AddWebPart(wp7, "left", 3);

mgr.AddWebPart(wp7, "left", 3);

// Internal Contacts List

SPList list2 = currentWeb.Lists["Internal Contacts"];

// Instantiate the web part

ListViewWebPart wp2 = new ListViewWebPart();

wp2.ZoneID = "Right";

wp2.ListName = list2.ID.ToString("B").ToUpper();

wp2.ViewGuid = list2.DefaultView.ID.ToString("B").ToUpper();

mgr.AddWebPart(wp2, "Right", 3);

// Announcements List

SPList list3 = currentWeb.Lists["Announcements"];

// Instantiate the web part

ListViewWebPart wp3 = new ListViewWebPart();

wp3.ZoneID = "Right";

wp3.ListName = list3.ID.ToString("B").ToUpper();

wp3.ViewGuid = list3.DefaultView.ID.ToString("B").ToUpper();

mgr.AddWebPart(wp3, "Right", 4);

// Links List

SPList list4 = currentWeb.Lists["Links"];

// Instantiate the web part

ListViewWebPart wp4 = new ListViewWebPart();

wp4.ZoneID = "Right";

wp4.ListName = list4.ID.ToString("B").ToUpper();

wp4.ViewGuid = list4.DefaultView.ID.ToString("B").ToUpper();

mgr.AddWebPart(wp4, "Right", 5);

currentWeb.Update();

}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{

/* no op */

}

public override void FeatureInstalled(SPFeatureReceiverProperties properties)

{

/* no op */

}

public override void FeatureUninstalling(SPFeatureReceiverProperties properties)

{

/* no op */

}

}

}

clip_image010

11. Next we need to add a feature stapler so the code and thus the web parts will be added to default.aspx when the site is provisioned!

12. Add A new feature folder to the WSPBuilder solution

a. Name it ABCTeamSiteRecStapler

i. Add the following feature code:

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

<Feature Id="{5FAFFFF9-50BF-4fea-BC70-7AF3266AFC4E}"    note: make sure the guid points to the feature receiver

Title="$Resources:MultiLangStaplingFeatureName"

Description="$Resources:MultiLangstaplingFeatureDescription"

Version="12.0.0.0"

Scope="Site"

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

DefaultResourceFile="_Res">

<ElementManifests>

<ElementManifest Location="elements.xml"/>

</ElementManifests>

</Feature>

13. Add the following elements.xml file

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

<FeatureSiteTemplateAssociation

Id="{3D6DC874-154C-4e3d-AE0E-9A3F21D69136}"

TemplateName="ABCTeamSite#0" />

</Elements>

clip_image012


List Definitions Using SharePoint Solutions Generator 2008

1. Start SharePoint Solutions Generator

a. Create List Definition

clip_image002

clip_image004

clip_image006

b. The two custom lists are Internal Contact (Custom list) and Tasks (Custom View)

c. Hit defaults’ to finish

d. VS 2008 Solutions Generator will provide a link to the generated solution

clip_image008

2. Create two Feature folders in the WSPBuilder Solution

a. Tasks

b. InternalContacts

clip_image010

3. Copy the two folders created by the Solutions Generator into these folders

clip_image012

4. Notice the custom view in the tasks list definition

a. In addition, The ListDefinition.xml file.

b. We will use this to create the feature so:

i. Move this to the next folder up on both lists

clip_image014

5. Next we will create the feature files

a. Add a new xml file to the feature folder on both lists

clip_image016

b. Copy the following xml into the file:

<?xml version="1.0"?>

<Feature Id="{770CB6F9-FC52-4041-872E-83E9685B2F7A}"

Title="Internal Contacts"

Description=""

Version="1.0.0.0"

Scope="Web"

ImageUrl="ABCteamSite/company1.jpg"

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

<ElementManifests>

<ElementManifest Location="ListDefinition.xml" />

</ElementManifests>

</Feature>

clip_image018

c. Do the same for the Tasks list

d. Be sure to change the GUID on the list feature

e. The image should be the same but with a different number for each feature.

i. I use a small transparent gif of the company logo

ii. If I have 10 features I will have 10 images the same just numbered sequentially

iii. The reason is if someone deactivates a feature all the other features will show with a broken image if they all use the same image (Experience)

6. The List definition File will be the same that was created by the Solution generator

clip_image020

clip_image022

7. The real workhorse of the list definition is the schema.xml file and is beyond the scope of this blog entry!

8. Now we need to create list instance features for each list in the site:

a. 6 lists

i. Tasks

ii. Internal Contacts

iii. Calendar

iv. Links

v. Announcements

vi. Team Discussions

b. They will all be similar so a copy and paste would be appropriate

c. The custom lists instances will have their feature GUID exposed in their elements.xml file while list instance based upon built in list templates will reference the built in feature GUIDS.

d. Custom Lists (Tasks)

9. Built-in List (Announcements)

clip_image024

clip_image026

a. Notice the feature ID in the list instance reflects a built-in List feature ID: 0BFEA71-D1CE-42de-9C63-A44004CE0104

b. They are all the same until the last three digits in this case 104

10. Custom List Internal Contacts List template feature

clip_image028

clip_image030

a. Notice that the list instance points to the same feature as the list template