How to develop a List Definition in SharePoint 2010

This tutorial will guide you through creating a SharePoint 2010 List Definition using Visual Studio 2010.

Open Visual Studio 2010

From the Visual Studio 2010 menu, select File --> New --> Project....
Select the Visual C# --> SharePoint 2010 --> List Definition project template.
Enter "Music" in the Name textbox and click on OK.

In the SharePoint Configuration Wizard select "Deploy as farm solution" and click on "Next"




















Enter "Music" in the What is the display name of the list definition? textbox.
Select Custom List in the What is the type of the list definition? drop-down box.




















In the Solution Explorer, expand ListInstance1 and open the Elements.xml file (do not confuse this Elements.xml file with the other Elements.xml file which is on the same level as ListDefinition1)


 
Within the ListInstance element change:
      the Title attribute to Music
      and change the TemplateType attribute to 10001.


Now, open the Elements.xml file which is on the same level as ListDefinition1.
Within the ListTemplate element, change the Type attribute to 10001 and add theDisallowContentTypes="FALSE" attribute, as shown below.

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

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Do not change the value of the Name attribute below. If it does not match the folder name of the List Definition project item, an error will occur when the project is run. -->
<ListTemplate
Name="ListDefinition1"
DisallowContentTypes="FALSE"
Type="10001"
BaseType="0"
OnQuickLaunch="TRUE"
SecurityBits="11"
Sequence="410"
DisplayName="Music"
Description="My List Definition"
Image="/_layouts/images/itgen.gif"/>
</Elements>

Insert the following xml into the top of the Elements element in the Elements.xml file. This XML describes the Music Item content type the list will store. 
<ContentType 

ID="0x010089E3E6DB8C9B4B3FBB980447E313CE94"
Name="Music Item"
Group="Custom Content Types"
Description="Music item content type."
Version="0">
<FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" />
<FieldRef ID="{cb55bba1-81a9-47b6-8e6c-6a7da1d25602}" />
<FieldRef ID="{0248c82f-9136-4b3a-b802-d0b77280b3bc}" />
<FieldRef ID="{aa4a82dd-5b32-4507-9874-4e1c7bca3279}" />
</FieldRefs>
</ContentType>

Insert the following xml into the top of the Elements element in the Elements.xml file. This XML describes the Fields the Music Item content type uses. 
<Field Type="Note" DisplayName="Description" Required="FALSE" NumLines="6" RichText="FALSE" Sortable="FALSE" ID="{cb55bba1-81a9-47b6-8e6c-6a7da1d25602}" StaticName="Album Description" Name="Album Description" Group="Custom Columns" />The Elements.xml file should now look like the following.


<Field Type="Text" DisplayName="Title" Required="FALSE" MaxLength="255" ID="{0248c82f-9136-4b3a-b802-d0b77280b3bc}" StaticName="AlbumTitle" Name="AlbumTitle" Group="Custom Columns" />


<Field Type="Text" DisplayName="Artist" Required="FALSE" MaxLength="255" ID="{aa4a82dd-5b32-4507-9874-4e1c7bca3279}" StaticName="Artist" Name="Artist" Group="Custom Columns" />












































In the Solution Explorer, open Schema.xml
Add the EnableContentTypes="TRUE" attribute to the List element inside of the Schema.xml file












Insert the following XML into the ContentTypes element in the schema.xml file. This XML describes the Music Item content type this list will store. 

<ContentTypeRef ID="0x010089E3E6DB8C9B4B3FBB980447E313CE94" />



















Insert the following XML into the Fields element. This XML describes the fields the list will store. These are directly related to the fields in the Content Type we added in the previous step. 

<Field Type="Note" DisplayName="Description" Required="FALSE" NumLines="6" RichText="FALSE"

Sortable="FALSE"
ID="{cb55bba1-81a9-47b6-8e6c-6a7da1d25602}" StaticName="Album Description"
Name="Album Description" Group="Custom Columns" />
<Field Type="Text" DisplayName="Title" Required="FALSE" MaxLength="255"
ID="{0248c82f-9136-4b3a-b802-d0b77280b3bc}"
StaticName="AlbumTitle" Name="AlbumTitle" Group="Custom Columns" />
<Field Type="Text" DisplayName="Artist" Required="FALSE" MaxLength="255"
ID="{aa4a82dd-5b32-4507-9874-4e1c7bca3279}"
StaticName="Artist" Name="Artist" Group="Custom Columns" /> 

Insert the following XML into the ViewFields element in the 2nd view, BaseViewID="1". This XML describes which fields should be visible in this particular view. BaseView 1 is set as the default view and this is the view the newly created fields should display in.

<ViewFields>

<FieldRef Name="Attachments"></FieldRef>
<FieldRef Name="LinkTitle"></FieldRef>
<FieldRef Name="Album Description"></FieldRef>
<FieldRef Name="AlbumTitle"></FieldRef>
<FieldRef Name="Artist"></FieldRef>
</ViewFields> 
  
 From the menu, select Build 
From the menu, select Deploy
Open your target SP2010 website in a browser session. 
Under Lists in the left-side navigation, click on Music to view the new list.



















How to delete multiple attachments in SharePoint

using (SPSite site = new SPSite(@"http://moss2010/WorkFlowCenter"))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;
                    SPList list = web.Lists["Booking"];
                    SPListItem item = list.GetItemById(10);

                    SPAttachmentCollection attachmentCollection = item.Attachments;
                    List<String> attachmentList = new List<string>();

                    for (int i = 0; i < attachmentCollection.Count; i++)
                    {
                        attachmentList.Add(attachmentCollection[i]);
                        
                    }

                    for (int i = 0; i < attachmentList.Count; i++)
                    {
                        item.Attachments.Recycle(attachmentList[i]);
                    }
                    item.Update();
                }
            }




RegistrationType & RegistrationId in SharePoint 2010 declarative Ribbon customizations

RegistrationType and RegistrationId attributes of <CustomAction> element are being used in the declarative SharePoint 2010 user interface customizations when they target any list/library related Ribbon UI.
RegistrationType and RegistrationId attributes works in pair only. They form a combination where RegistrationType is a type of criterion for applying customization and RegistrationId is a value of this criterion to compare with content.



What is "content"?
What is "content" with respect to declarative SharePoint Ribbon customization? It's a list/library or a single list item. Where does SharePoint assess that current declarative Ribbon customization is applicable to content? It does it on any page where there is at least one of the standard list view web parts (ListViewWebPart, DataViewWebPart etc.) or list item web parts (ListFormWebPart, DataFormWebpart etc.). It's true for all standard (i.e. generated by SharePoint) list view forms and new/edit/display list item forms because they contain one of such web parts. It also works for any page where such web part is added manually.
For the list view web parts SharePoint analyzes the list itself and its content types but ignores items in this list. In case of single list item web part SharePoint analyzes the list, the content type of list item and a file if the list item is document library item.

Unsupported content
There are some types of content where SharePoint doesn't apply any SharePoint Ribbon declarative customizations for some unknown reasons. One of such types is "Picture Library" (base list template 109). The picture library exception is hardcoded in SharePoint code. So you can apply your customization to image files in general document library by file extension (see the FileType section below) or by content type but you can't apply customizations if image files stored in picture library.

RegistrationType
RegistrationType can have one of four hardcoded values – "List", "ContentType", "FileType" and "ProgId" with exact letter case. The applicability of these values depends on context – is current content list or library or a single list item or a single library item (i.e. item with file):
RegistrationType List List Item Library Library Item
List + + + +
ContentType + + + +
FileType


+
ProgId


+
RegistrationId
RegistrationId is a string which format depends on the chosen RegistrationType.

"List"
Despite the "List" name you can't use this type of registration with list unique identifier, title, URL or any other list property. RegistrationId for the "List" RegistrationType must correspond to the base list template identifier of the list where customization should appear. This identifier is always an integer from the hardcoded range. So this type of registration allows limiting of declarative Ribbon customization to some class of lists but not to specific list instances.

What if you need to apply customization to a single list instance?
You can use other types of registration. For example you can create an unique content type for your list and apply customization to this content type (see the ContentType section below). Another way is to customize Ribbon programmatically.

While applying Ribbon customization on pages with standard list view webparts and list item web parts (see the "What is "Content" section above) SharePoint compares RegistrationId value in "List" registrations to a base value of Microsoft.SharePoint.SPListTemplateType enumeration member which is stored in Microsoft.SharePoint.SPList.BaseTemplate property of the list being assessed.

According to some information there is additional range of identifiers not included in SPListTemplateType enumeration. These identifiers don't used in Ribbon customization for standard webparts and it's unclear how and where they are used.

The most complete known list of base template identifiers including non standard (i.e. not included in SPListTemplateType enumeration) is listed here.
Here is an example of registration (please note that in current example and in following examples other required attributes of <CustomAction> element ommited for simplicity):

<CustomAction RegistrationType="List" RegistrationId="101">

"ContentType"
RegistrationId for this type of registration must correspond to content type identifier of the content where customization is required. Content type identifier is a string of specific format and looks like «0x0100A33D9AD9805788419BDAAC2CCB37509F». While applying customization in the context of a single list item SharePoint gets this identifier from SPListItem.ContentTypeId property. In the list context Sharepoint gets it from the content type collection of current list (SPListItem.ContentTypes). The case is important. The value of RegistrationId must be equal to the whole target content type id or at least to the beginning of it. Content type identifiers have a recursive structure and always contain identifiers of all parents. So this type of registration is always applied not only to target content type itself but also to all its descendants.

In the list context the customization will appear if there is at least one content type covered by specified RegistrationId. Existing items in the list context are not analyzed – only content types of the list matter.

In the context of single list item the customization will appear only if the content type of current item is right.

Here is an example:
<CustomAction RegistrationType="ContentType" RegistrationId="0x0100A33D9AD9805788419BDAAC2CCB37509F">

"FileType"
For this type of registration RegistrationId must be an extension of the file for current list item. Because a file of current list item is analyzed this type of registration available in the context of single library item (i.e. item with file) only. The case is ignored and the leading dot is not needed before extension:
<CustomAction RegistrationType="FileType" RegistrationId="docx">

"ProgId"
For this type of registration RegistrationId must be an identifier of application where the content of the library item file created. SharePoint extracts this application identifier from list item file and saves in system field with name "HTML_x0020_File_x0020_Type". So similar to FileType registration ProgId registration is available only in context of single library item. The case is not important.

Which files can be targeted? You can target files of XML or HTML based formats. An example of XML-based format is MS Word document saved as "Word XML-document". It contains ProgId in the beginning of the body in the following tag:
<?mso-application progid="Word.Document"?>
An example of HTML-based format is MS Word document saved as "HTML-page" – the ProgId in this case will be saved in such tag:
<meta name=ProgId content=Word.Document>
You can test if SharePoint could extract ProgId from the given file by getting it from list item programmatically:
string ProgID = (string)listItem[SPBuiltInFieldId.HTML_x0020_File_x0020_Type];
or
string ProgID = (string)listItem["HTML_x0020_File_x0020_Type"];
Then you can use obtained value in your customization:
<CustomAction RegistrationType="ProgId" RegistrationId="Word.Document">





Workflow status codes (used for view filter) of SharePoint

Here is the list of the status codes used to maintain the workflow statuses. This list might be useful when anyone creates a view based on workflow status. If you are creating a view for a list filtered by the workflow status, you would need use the number rather than the words (e.g. 2 instead of In Progress, 4 instead of Cancelled etc)

Status
Value
Not Started 0
Failed on Start 1
In Progress 2
Error Occurred 3
Canceled 4
Completed 5
Failed on Start (retrying) 6
Error Occurred (retrying) 7
Canceled 15
Approved 16
Rejected 17

 



how to setup Document Library template via code in SharePoint

Here is simple example how to setup Document Library template via code:

using (SPSite site = new SPSite("http://mycoolsite")) { 
     using (SPWeb web = site.OpenWeb()) 
     { 
          SPList list = web.Lists["MyLibrary"]; 
          SPDocumentLibrary docLib = (SPDocumentLibrary) list; 
          docLib.DocumentTemplateUrl = "/mylibrary/Forms/NewTemplate.doc"; 
          docLib.Update(); 
     } } 
However, if template is outside Document Library Forms folder you will receive error:
Invalid template URL.
The template must exist in the Forms directory of this document library. Create the template in the Forms directory, and then re-type the Web address. Note that you cannot move or copy a template into the Forms directory.
You can provide document template to forms folder of Document Library via feature using module element. Your feature element.xml would look something like this:
<?xml version="1.0" encoding="utf-8" ?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
    <Module  
        Name="MyLibrary" SetupPath="Features\MyLibrary" 
        Path="" Url="_cts/MyLibrary"> 
        <File Url="NewTemplate.doc" /> 
    </Module> </Elements> 
IMO this is not right solution for you. I suggest that you deploy custom content type already associated to custom template and when you create new library you just add content type to it. There are plenty articles on web covering this. Here are few (found by simple google search) to get you started:
Or you can use custom document templates (as you pointed in your question):

Supply a doc template for a Content Type in a Feature in SharePoint

So if you have played with MOSS features at all, you probably know that it is possible to deploy all kinds of customizations as features: custom actions, workflows, list definitions, content types, etc. In my demo, I wanted a feature that contained a content type for a document library to also provide a new word document "InterviewConfirmation.docx" as its template. Surprisingly, this little extra is a bit hard to find. You can easily find how to declare the content type, how to add site columns to it, and how to bind it to a list. But supply a document template. Nope. Nada. So here is a nice piece of info:
First in the feature.xml file, you need to include your element manifest where the content type is stored. And you also need an elementfile declaration for the document template:
<ElementManifests>
    <ElementManifest Location="hrdocs.xml"/>
    <ElementFile Location="InterviewConfirmation.docx"/>
  </ElementManifests>
Then in the element manifest, the following declares a Interview Confirmation content type that derives from document (0x0101). You then use the content type's DocumentTemplate element to build the link to the file. But you need to use a module to get the file loaded into the site collection. After some exploring with SharePoint designer, you can see where these files go. They are stored in the _cts folder in a folder named the same as the content type. You can use a Module element to provision the file.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ContentType ID="0x01010D"
               Name="Interview Confirmation"
               Group="HR Docs"
               Description="Interview Confirmation"
               Version="0">
    <DocumentTemplate TargetName="InterviewConfirmation.docx"/>
  </ContentType>
  <Module Name="HRDocs" Url="_cts/Interview Confirmation" RootWebOnly="TRUE">
    <File Url="InterviewConfirmation.docx" Name="InterviewConfirmation.docx" Type="Ghostable"></File>
  </Module>
</Elements>

The difference between SPWeb.Users and SPWeb.SiteUsers in SharePoint

Getting lists of users is easy, when you know how. There are some subtle differences that might trip you up when you’re looking to retrieve a list of users from a SharePoint Site or Portal Area. The SPWeb class has two properties, Users and SiteUsers. Here’s the documentation on each:

SPWeb.UsersGets the collection of user objects belonging to the Web site.
SPWeb.SiteUsersGets the collection of all users belonging to the site collection.

There’s just a *slight* difference in wording here. After all, SPWeb.Users gives me the user objects of the web site right? Not really.
As this was something that was bugging me with a problem I had, I decided to do a small spike to prove (or disprove) what was going on. When you have a design problem or a technical complexity that has to be addressed (before staring your work to reduce the risk) you create a spike, a small side project apart from your current work. The spike solutions are developed to solve or explore critical problems. Ideally those programs will be thrown away once every developer gets a clear idea about the problem.
So we’ll create the SSWP[1] with the following RenderWebPart method defined:
   63         protected override void RenderWebPart(HtmlTextWriter output)
   64         {
   65             SPSite site = SPControl.GetContextSite(Context);
   66             SPWeb web = site.OpenWeb();
   67             output.Write(string.Format("<strong>Information</strong><br>User Count={0}, Current User ID={1}</p>",
   68                 web.Users.Count, web.CurrentUser.ID));
   69 
   70             StringBuilder sb = new StringBuilder();
   71             sb.Append("<table border=1>");
   72             sb.Append("<tr><td colspan=3><strong>SPWeb.Users</strong></td></tr>");
   73             sb.Append("<tr><td>ID</td><td>LoginName</td><td>Name</td></tr>");
   74             foreach (SPUser user in web.Users)
   75             {
   76                 sb.AppendFormat("<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>",
   77                     user.ID,
   78                     user.LoginName,
   79                     user.Name);
   80             }
   81             sb.Append("</table>");
   82             output.Write(sb.ToString());
   83 
   84             sb = new StringBuilder();
   85             sb.Append("<table border=1>");
   86             sb.Append("<tr><td colspan=3><strong>SPWeb.SiteUsers</strong></td></tr>");
   87             sb.Append("<tr><td>ID</td><td>LoginName</td><td>Name</td></tr>");
   88             foreach (SPUser user in web.SiteUsers)
   89             {
   90                 sb.AppendFormat("<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>",
   91                     user.ID,
   92                     user.LoginName,
   93                     user.Name);
   94             }
   95             sb.Append("</table>");
   96             output.Write(sb.ToString());
   97 
   98             try
   99             {
  100                 SPUser currentUser = web.SiteUsers.GetByID(web.CurrentUser.ID);
  101                 output.Write(string.Format("</p>Current user is {0} (ID={1})",
  102                     currentUser.LoginName, currentUser.ID));
  103             }
  104             catch(SPException e)
  105             {
  106                 output.Write(string.Format("</p>Error getting user ({0})", e.Message));
  107             }
  108         }
So what’s this thing doing? Not much but basically:
  • Get a reference to the SPSite object using the GetContextSite method of SPControl
  • Open the SPWeb object using the OpenWeb method of the SPSite
  • Write out some information about the number of users and current user id
  • Create a small table with the id, login name, and display name of each user in the SPWeb.Users collection
  • Do the same thing but with the SPWeb.SiteUsers collection
  • Retrieve the SPUser object from the Web using the GetByID method and display information about that user
It’s the last part (wrapped in a try/catch block) that you should pay attention to.
Here’s the output of the Web Part logging in as LOCALMACHINE\Administrator when displayed in a SharePoint Portal Server Area:

And here’s the same Web Part when displayed in a Windows SharePoint Server site:

When you create an area or site, several users are automatically added behind the scenes. In the case of an area on the portal, you’ll see that SiteUsers contains more names than what’s in the WSS site. It adds in a “BUILTIN\Users” domain group (ID 4) and adds the “NT AUTHORITY\Authenticated Users” at the end (ID 6) in an area. In a site, “NT AUTHORITY\Authenticated Users” takes up the #4 slot, and the “BUILTIN\Users” is nowhere to be found.
Also when you create a site, it adds the creator (in this case “Administrator”) to the list of users (go into Site Settings | Manage Users to see the list). In area, the creator of that area shows up in the SPWeb.Users list, but it’s not present on the “Manage Security” page.
Here’s a rundown on the user ID slots that are filled in automatically:

IDDescription
1User who created the site or area
2Not sure on this one? Sorry.
3Application Pool Id, crawler account.
4BUILTIN\Users on Portal, NT AUTHORITY\Authenticated Users on WSS
5Additional individual users you add to the site, or any new users who happen along.
6NT AUTHORITY\Authenticated Users on Portal only, otherwise the next user you add or visits the area.


Note: these values are from most setups I’ve done, the way you configure your portal (turning on anonymous for example) might change these values somewhat so use them as a guideline, not a set of stone tablets that someone hands down to you after taking a siesta in the mountains.
You might say, “But Bil, I really only want the current user so SPWeb.CurrentUser should be good enough right?”. Maybe. What if you want to retrieve a user by ID (that might be stored somewhere else, but corresponds to the same ID # in the site). Or you want to display all the users in the site. This will become important when I blog later this week about Impersonation (everyone’s favorite topic).
So here’s the same Web Part, but being rendered by a regular Joe user (spsuser) who has read access to the site. He’s been added manually to the WSS site, but he’s a member of the Readers group at the Portal and doesn’t show up in the SPWeb.Users list. First, the Portal Area rendering:

Now the WSS site rendering:

Remember when I said to pay attention to that try/catch block? If you had used a bit of code like SPWeb.Users.GetByID (instead of using the SiteUsers property) you would have got an exception thrown while looking for a user with an ID of 5. As you can see in the SPWeb.Users list, it doesn’t exist because this will only display domain groups and indvidual users who have been manually added to the site (and Administrator got added automatically when the site got created).
Another thing that’s interesting is that if you did add “spsuser” to the site manually he would show up in the SPWeb.Users list above. However if you removed him, he would vanish from SPWeb.Users but he would still show up in the SPWeb.SiteUsers list. His ID # is reserved so the next user you added manually would have an ID of #6 (in the case of the site) and if you re-added “spsuser” at a later date, he would re-appear in both lists with an ID of #5.
So in a nutshell… if you’re going after a list of all users or want to retrieve a user from the list but he’s part of a group, then use the SiteUsers property instead of Users. Otherwise, he might not be there.
Okay, this post might have been a little confusing as we’re flipping around between Users and SiteUsers and all that but hopefully the code and images describe it for you. Let me know if you’re totally confused, otherwise… enjoy!
[1] Super-Simple-Web-Part: Just a web part where we hard code the values in the RenderWebPart method. Nothing fancy here. No Domain Objects. No DTOs. No Layers. Just write out some code.

How to use SharePoint properties to save values instead of web.config with sharepoint 2010/2007

I had two web parts in the same project but I wanted them to share settings. As my experience as an ASP.Net developer was pointing me to use the web.config file, instead I thought SharePoint must have something similar to this without me having to mess with the web.config file. After searching and posting on MSDN, I did finally find a solution that uses the properties of SharePoint Sites and Webs.
Below is a sample code that stores the Employee Profile Group in the web so two web parts and an InfoPath form can access the values.
The GetWebProperty function creates the property if it doesn't exist, and if it does it'll return the string value. It's important to call this function before setting a web property, especially the first time it's run so the property is created. I suppose you could technically put the same creation functionality in the SetWebProperty as well, but I was using Get (getting values into my webpart) before my Set (using edit pane to view it).
string employeeProfile = GetWebProperty("EmployeeProfileUserGroup");
SetWebProperty("EmployeeProfileUserGroup", pe.DisplayText);
static string GetWebProperty(string Name)
{
 using (SPSite site = new SPSite(SPContext.Current.Site.ID))
 {
  SPWeb web = site.OpenWeb(SPContext.Current.Web.ID);
  if (web.Properties[Name] == null)
  {
   web.AllowUnsafeUpdates = true;
   web.Properties.Add(Name, "");
   web.Properties.Update();
   web.AllowUnsafeUpdates = false;
   return "";
  }
  else
  {
   return web.Properties[Name].ToString();
  }
 }
}
void SetWebProperty(string Name, string Value)
{
 using (SPSite site = new SPSite(SPContext.Current.Site.ID))
 {
  SPWeb web = site.OpenWeb(SPContext.Current.Web.ID);
  GetWebProperty(Name); //ensure the property exists
  web.AllowUnsafeUpdates = true;
  web.Properties[Name] = Value;
  web.Properties.Update();
  web.AllowUnsafeUpdates = false;
 }
}
If you would like to share the same properties across multiple webs, use the Site.RootWeb object and store all properties there!