As I have been continuing the development of the end-to-end solution, I have been doing a lot of work with the SharePoint web services -- specifically the lists.asmx web service used for working with lists in SharePoint.
Anyone who has dealt with this web service knows that SharePoint is happy to provide you with a lot of XML to parse. In an effort to make these easier, I began exploring the use of LINQ to XML since I'm not the biggest fan of writing my own XPath queries. In this quest, I came across two articles that were very useful and I hope you will find these useful as well.
Using LINQ to XML in combination with the Lists.asmx
This article describes how to use LINQ to XML in conjunction with the GetListCollection() method of the lists.asmx web service. I made use of this in my client application to populate a combo-box.
SharePoint to Linq
Using this site, I was able to learn how to use LINQ to XML to build queries and anonymous types for data-binding list data to my client application.
I'm in the process of refactoring some of the end-to-end solution I've been working on, so that has slowed me down a bit in getting the next posts up. I hope to get the refactoring completed soon so I can share the steps I've taken to create an end-to-end solution in SharePoint 2007.
09 October 2009
Resources: LINQ to XML for SharePoint Lists Web Service
Posted by
Chris
at
10:45 AM
0
comments
24 September 2009
End-To-End Solution – Custom List Definition
Today, I will continue to discuss the end-to-end solution I’ve been building for my organization. In Part 1, I began by describing my requirements and started building my solution. In the last post, I began building a feature receiver that fires when the feature is activated on my site collection.
In the last few posts, I think I became guilty of trying to cover too much at a time, so I’ve decided to limit this post the definition of my custom list. This is so I slow down and spend more time discussing the parts of the solution. I hope this change will allow me to make some things a little more understandable.
In the solution’s FEATURE folder, a new XML file is created that will contain the definition of my custom list. Again, I will set the schema of this file to use the wss.xsd located in the [12 Hive]/TEMPLATES/XML/ folder. To do this, in the properties window of the file and locate the Schema property. Click on the ellipses to show all of the currently available schema definitions. Click on Add and navigate the the [12 HIVE]/TEMPLATES/XML/wss.xsd file. This will enable intellisense for the document.
This is the definition of the list template:
1: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">2: <ListTemplate Type="10800"3: BaseType="0"4: Name="CategoryList"5: DisplayName="Category List"6: Description="Contains a list of categories"7: SecurityBits="11"8: VersioningEnabled="false"9: Hidden="TRUE"10: DisableAttachments="true"11: Category="Custom Lists"12: Sequence="100"/>13: </Elements>
Since this file will define a new element in SharePoint, the root node of my XML file is Elements. Inside the Elements node, the ListTemplate node is used to define a new list template to be used on the SharePoint site. The list templates show up when creating a new content for the SharePoint site.
I will be honest, I’m not sure what some of the attributes are for and welcome feedback from any that do, but I will discuss the ones that I do know.
First, Type is an identifier for the type of list. Microsoft recommends that you use any five digit number over 10000. I randomly selected 10800 because our organization plans to start at 10800 for any custom lists defined through features.
Name is the internal name of the list. This will also be the name of the folder that contains the schema file for the list. DisplayName is the list that will be displayed on the content creation screen in SharePoint. Description contains the description that will be displayed when the list is highlighted when creating content.
VersioningEnabled will turn on versioning in this list by default when it is created. Because this list will be synchronized with an external database, versioning will help make sure that any category can be rolled back to a previous version if needed.
Hidden will determine if this list type shows up to be created in the user interface. By setting this to TRUE, the list definition will not be displayed. This is being done because there should only be one Category list on the site, and that one will be created when the feature is activated.
DisableAttachments when set to true will turn off attachments on any instances of this list. Attachments will not be needed in this list, so they will be turned off by default.
Category is one of the visible category columns on the create content page. Sequence sets the display order in the UI. Since this will not be used in the current solution, they will not be necessary and are here for demonstration purposes only.
A new folder needs to be added to the root of the current solution folder. This folder should be the same name at the name used in the list definition, found on line 4.
4: Name="CategoryList"
My solution hierarchy should be:
- Project
- 12
- TEMPLATE
- FEATURES
- KBRepository
- CategoryList
- KBRepository
- FEATURES
- TEMPLATE
- 12
To begin with the schema.xml file used for the list, start by copying the schema.xml file from [12 Hive]/TEMPLATE/FEATURES/CustomList/CustomList/schema.xml. Place a copy of this file into the CategoryList folder and begin making customizations to the list. You can then follow the information provided in Andrew Connell’s blog on a quicker way to create list definitions.
Finally, the FEATURE.XML file needs to be updated to contain a reference to the new files that have been added to the solution.
1: <Feature xmlns="http://schemas.microsoft.com/sharepoint/"2: Scope="Site"3: Creator="Software Development"4: Id="386CF027-6AC7-460a-A7D6-4E503B2B6293"5: Description="Sets up this site collection for KB"6: Title="KB Repository"7: Version="1.0.0.0">8: <ElementManifests>9: <ElementManifest Location="KBItemFields.xml"/>10: <ElementManifest Location="KBContentTypes.xml"/>11: <ElementManifest Location="CategoryList.xml"/>12: </ElementManifests>13: </Feature>
The next post will complete the feature receiver.
Posts in series:
Posted by
Chris
at
11:21 PM
2
comments
22 September 2009
End-To-End Solution – Feature Receiver Part 1
As promised, this post will be focused on creating a feature receiver that will allow me to perform some actions when the feature is activated or deactivated on the site. I plan to go a bit slower now that I’m posting code so that I make sure I explain both the code and the decision process behind the code. As stated earlier, I welcome feedback, especially if I’m missing an easier or “best-practices” way of performing my task. Please be sure to check out my first post in this series for more information on the scope of this project and the initial feature definition.
In the existing project, a new class file was created. This was accomplished by right clicking on the project node in solution explorer and choosing add new class. I want the code file to exist outside of the 12 folder since I do not want the code file to be included in my solution file. I named the class KBFeatureReceiver.
At this point, references to the SharePoint assemblies need to be added. If you are developing on a machine with SharePoint installed (highly recommended) you can simply right click on the project node and choose to add references. Browse the .Net tab for Windows SharePoint Services and add this as a new reference to the project.
Inside the newly created class file, you will need to add some Imports (C# Using) statements to remove the necessity of typing full namespaces.
1: Imports Microsoft.SharePoint
The class will need to inherit from the SPFeatureReciever class located in the SharePoint assembly. There are four methods that you must override to implement a custom feature receiver:
- FeatureActivated – occurs when the feature is activated. This is the place where I will want to create my custom list and special site column.
- FeatureDeactivating – occurs when the feature is deactivated. Here I will place code that will clean-up any customizations.
- FeatureInstalled – occurs when the feature is installed on the server.
- FeatureUninstalling – occurs when the feature is uninstalled from the server.
Thus far, this is the code I have:
1: Imports Microsoft.SharePoint
2:
3: Public Class KBFeatureReceiver
4: Inherits SPFeatureReceiver
5:
6: Public Overrides Sub FeatureActivated(ByVal properties As SPFeatureReceiverProperties)
7:
8: End Sub
9:
10: Public Overrides Sub FeatureDeactivating(ByVal properties As SPFeatureReceiverProperties)
11:
12: End Sub
13:
14: Public Overrides Sub FeatureInstalled(ByVal properties As SPFeatureReceiverProperties)
15:
16: End Sub
17:
18: Public Overrides Sub FeatureUninstalling(ByVal properties As SPFeatureReceiverProperties)
19:
20: End Sub
21: End Class
Personally, I like to segment my code into smaller areas of responsibility, so I’m going to create several methods to perform additional tasks. In order to maintain a reference to the SPWeb object, I will make this a parameter of each method. Based on my requirements, I need a method to create a list, remove a list, create a site column and remove a site column. Remember, as you work with the SPWeb and SPSite objects, it is very important to make sure these objects are disposed of properly.
Notice that each of the overridden methods has a single parameter – properties. Using this parameter, you can gain a reference to the calling SPSite object via properties.Feature.Parent. The call will return a generic object that should be cast into an SPSite object. From this point, the SharePoint OM is available to the code. The FeatureActivated method now looks like the following:
1: Public Overrides Sub FeatureActivated(ByVal properties As SPFeatureReceiverProperties)
2: Using mSite As SPSite = CType(properties.Feature.Parent, SPSite)
3: Using mWeb As SPWeb = mSite.RootWeb
4: Dim list As SPList = CreateCategoryListInstance(web)
5: CreateCategoryLookupSiteColumn(web, list)
6: End Using
7: End Using
8: End Sub
The method makes use of Using statements. This statement will help by automatically disposing of disposable objects. On line 4 a call is made to create a new category list instance and return the newly created list. The reference to the newly created list is then passed as a parameter to the method that creates the category lookup column.
Here the the CreateCategoryListInstace method:
1: Protected Function CreateCategoryListInstance(ByVal web As SPWeb) As SPList
2: ' Create a new list for the categories and store the GUID for use later.
3: Dim list As SPList
4: Dim listID As Guid
5:
6: listID = web.Lists.Add("Category List", & _
7: "List Description", "Lists/HICUPCats", & _
8: "386CF027-6AC7-460a-A7D6-4E503B2B6293", " & _
9: 10800, 100)
10:
11: ' Store the GUID of the new list for clean-up later if needed.
12: If web.AllProperties.ContainsKey("HICUPCategoryList") = True Then
13: web.AllProperties("HICUPCategoryList") = listID.ToString()
14: Else
15: web.AllProperties.Add("HICUPCategoryList", listID.ToString())
16: End If
17: web.Update()
18:
19: ' Return the list
20: Return list
21: End Function
First, a new list is created (Lines 6-9). The creation of the list makes use of a custom list definition that is defined in the feature (more about this in a future article). For simplicity, you can create replace 10800 on line 9 with the TypeID for a custom list (I believe it is 100).
After the list is created, the GUID for the list is stored as a property of the SPWeb.AllProperties HashTable. This will allow code that will be developed later to quickly find this list without the need of iterating through the lists on the site or hoping the name of the list hasn’t been changed. Line 17 makes a call to SPWeb.Update() which will apply the settings up to this point.
Here is the final code that will be discussed for this post, CreateCategoryLookupSiteColumn:
1: Protected Function CreateCategoryLookupSiteColumn(ByVal web As SPWeb, & _
2: ByVal list As SPList) As Boolean
3: Try
4: ' Define the format for this lookup field
5: Dim fieldXMLFormat As String
6: fieldXMLFormat = "<Field ID=""{{3324B767-6CB6-4789-A0ED-60FB39BAC648}}""" & _
7: " Name=""SWBTS_KBCategory""" & _
8: " StaticName=""SWBTS_KBCategory""" & _
9: " DisplayName=""Category"" Type=""Lookup"" List=""{0}""" & _
10: " ShowField=""Title"" LCID=""1033""" & _
11: " Group=""HICUP Knowledge Base Columns""" & _
12: " UnlimitedLengthInDocumentLibrary=""false"" />"
13:
14: ' Using the format and apply the category list GUID to the string
15: Dim fieldXML As String = String.Format(fieldXMLFormat, list.ID.ToString())
16:
17: ' Add the field to the site columns collection
18: web.Fields.AddFieldAsXml(fieldXML)
19: web.Update()
20: Return True
21: Catch ex As Exception
22: Return False
23: End Try
24: End Function
Lines 6-12 are used to create the XML that will be used to define the lookup column. This information follows the standard field definition schema that is used in feature definitions. Line 15 adds the GUID of the Category List to the XML. Line 18 adds the field to the current SPWeb and the all important SPWeb.Update() method is called in line 19 to apply these changes.
The original idea was to use a feature to create this field definition, but experimentation never resulted in a successful lookup field. This was the best way in the time allotted to accomplish the goal.
In my next post I plan on defining the deactivating methods, updating my feature with a custom list definition and adding the lookup site column to the KB Article Content Type defined in the previous post.
Again, please provide any feedback or comments.
Posted by
Chris
at
10:15 PM
0
comments
21 September 2009
SharePoint 2010 Preview
Several weeks ago, Microsoft released some information through videos for the up and coming release of SharePoint 2010. If you haven’t already viewed these videos, what are you waiting for?
In short, one of the features that has been announced for developers is a first class development environment for SharePoint. One can only hope we either have better remote debugging support or the ability to install a “SharePoint Developer Version” on our Windows 7 desktops, but the ability to visually design web parts is a huge plus in my book. (I know, I know, some of you hard-core server control developers are disappointed).
Much more information should become available throughout the SharePoint conference – and while we’re on that topic, if you want to buy me a ticket to the conference I would gladly accept the opportunity to go!
Posted by
Chris
at
10:48 PM
0
comments