Archive

Archive for April, 2009

How to change the Author of a Document that is stored in a Document Library?

April 20, 2009 3 comments

Apparently there is’nt a straight forward way to achieve this. Below is how you can do it though:

SPList list = web.Lists[“myList”];

list.Fields[“Author”].ReadOnlyField = false;

SPListItem item = list.Items.Add();

item[“Author”] = “value”;

item.SystemUpdate(false);

list.Fields[“Author”].ReadOnlyField = true;

WebPartPages: Programmatically adding a new web part page

April 5, 2009 3 comments

How do you add a WebPartPage or a BasicPage programmatically?

WebPartPages are stored in document libraries. It is simply a matter of adding the page to the document library you want. However, if you need to add a WebPartPage programmatically the same way SharePoint allows you to do via the UI then you can use the following method:


private void AddWebPartPage(string fileTitle, SPWeb web, SPList list, int webPartPageTemplate, string pageType, string folder)
{
    string postInformation =
    "<?xml version="1.0" encoding="UTF-8"?>" +
    "<Method>" +
    "<SetList Scope="Request">" + list.ID + "</SetList>" +
    "<SetVar Name="ID">New</SetVar>" +
    "<SetVar Name="Cmd">NewWebPage</SetVar>" +
    "<SetVar Name="Type">" + pageType + "</SetVar>" +
    "<SetVar Name="WebPartPageTemplate">" + webPartPageTemplate + "</SetVar>" +
    "<SetVar Name="Title">" + fileTitle + "</SetVar>" +
    "<SetVar Name="Overwrite">true</SetVar>" +
    "</Method>";
    string processBatch = web.ProcessBatchData(postInformation);
    if (processBatch.Equals("<Result ID="" Code="0">rn</Result>n"))
    {
        SPFile file = web.GetFile(list.RootFolder.Url + "/" + fileTitle + ".aspx");
        //if page was in subfolder then move it there
        if (!String.IsNullOrEmpty(folder))
        {
            file.MoveTo(fileTitle,
true);
        }
    }
}

Below are the WebPartPage templates you can specify:

  1. Full Page, Vertical
  2. Header, Footer, 3 Columns
  3. Header, Left Column, Body
  4. Header, Right Column, Body
  5. Header, Footer, 2 Columns, 4 Rows
  6. Header, Footer, 4 Columns, Top Row
  7. Left Column, Header, Footer, Top Row, 3 Columns
  8. Right Column, Header, Footer, Top Row, 3 Columns

Type could have the following possible values:

  1. WebPartPage
  2. BasicPage

For further details click here.

ListViewWebPart: Programmatically setting the ToolbarType property

April 5, 2009 10 comments

While working on a migration project we came across a requirement whereby ListViewWebPart’s had to be added to a page programmatically. The ListViewWebPart class has a property called toolbartype. Setting the toolbartype property sounds simple enough. I mean how difficult can it be to set a property?

The problem is that it is a read only property. We thought maybe we can set it via reflection. After searching a bit on google we found a couple of suggestions on how it could be done (most of them were solutions on how to set it to none). One method seemed to have solved our problem until we installed the latest infrastructure updates. Then it completely stopped working. However luckily for us Tony Stegeman and Brian Farhill came up with a solution.

Below you can see the complete solution that worked for us:


private static void SetToolbarType(SPView spView, string toolBarType)
{
    spView.GetType().InvokeMember(
"EnsureFullBlownXmlDocument",
    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
    null, spView, null, System.Globalization.CultureInfo.CurrentCulture);
    PropertyInfo nodeProp = spView.GetType().GetProperty("Node",
    BindingFlags.NonPublic | BindingFlags.Instance);
    XmlNode node = nodeProp.GetValue(spView, null) as XmlNode;
    XmlNode toolbarNode = node.SelectSingleNode("Toolbar");
    if (toolbarNode != null)
    {
        toolbarNode.Attributes[
"Type"].Value = toolBarType;
        // If the toolbartype is Freeform (i.e. Summary Toolbar) then we need to manually 
        // add some CAML to get it to work.
        if (String.Compare(toolBarType, "Freeform", true, System.Globalization.CultureInfo.InvariantCulture) == 0)
        {
            string newItemString = "";
            XmlAttribute positionNode = toolbarNode.OwnerDocument.CreateAttribute("Position");
            positionNode.Value =
"After";
            toolbarNode.Attributes.Append(positionNode);
            switch (spView.ParentList.BaseTemplate)
            {
                case SPListTemplateType.Announcements:
                    newItemString =
"announcement";
                    break;
                case SPListTemplateType.Events:
                    newItemString =
"event";
                    break;
                case SPListTemplateType.Tasks:
                    newItemString =
"task";
                    break;
                case SPListTemplateType.DiscussionBoard:
                    newItemString =
"discussion";
                    break;
                case SPListTemplateType.Links:
                    newItemString =
"link";
                    break;
                case SPListTemplateType.GenericList:
                    newItemString =
"item";
                    break;
                case SPListTemplateType.DocumentLibrary:
                    newItemString =
"document";
                    break;
                default:
                    newItemString =
"item";
                    break;
            }
            if (spView.ParentList.BaseType == SPBaseType.DocumentLibrary)
            {
                newItemString =
"document";
            }
            // Add the CAML
            toolbarNode.InnerXml = @"<IfHasRights><RightsChoices><RightsGroup PermAddListItems=""required"" /></RightsChoices><Then><HTML><![CDATA[ <table width=100% cellpadding=0 cellspacing=0 border=0 > <tr> <td colspan=""2"" class=""ms-partline""><IMG src=""/_layouts/images/blank.gif"" width=1 height=1 alt=""""></td> </tr> <tr> <td class=""ms-addnew"" style=""padding-bottom: 3px""> <img src=""/_layouts/images/rect.gif"" alt="""">&nbsp;<a class=""ms-addnew"" ID=""idAddNewItem"" href=""]]></HTML><URL Cmd=""New"" /><HTML><![CDATA["" ONCLICK=""javascript:NewItem(']]></HTML><URL Cmd=""New"" /><HTML><![CDATA[', true);javascript:return false;"" target=""_self"">]]></HTML><HTML>Add new " + newItemString + @"</HTML><HTML><![CDATA[</a> </td> </tr> <tr><td><IMG src=""/_layouts/images/blank.gif"" width=1 height=5 alt=""""></td></tr> </table>]]></HTML></Then></IfHasRights>";
        }
        spView.Update();
}

Programmatically getting the SPField object you just added

April 3, 2009 3 comments

When you add an SPField to an SPList programmatically using the SharePoint Object Model you can get the SPField object representing the newly added field via SPList.Fields[string displayName]. But this assumes that no other field with the same display name as the newly added SPField exists in the SPList.

The work around is listed below:

 

Guid fieldGuid = Guid.NewGuid();
string newFieldCAML =
 "<Field Type="Text" DisplayName="MyNewField" ID="+fieldGuid+"/>";
splist.Fields.AddFieldAsXml(newFieldCAML, false, SPAddFieldOptions.AddFieldInternalNameHint);
SPField newField = spList.Fields[fieldGuid];

You can use the AddFieldAsXml to add the new SPField. It takes a CAML string as a parameter in which you can specify your own Guid. Later you can use this Guid to retrieve the SPField you just added.

Meeting Workspace: How to programatically add tabs

April 3, 2009 1 comment

When you add a page via the UI it adds it as a WebPartPage to a hidden list called “Workspace Pages”. It also does something internally to create the Tab because simply progammatically adding the WebPartPage to the hidden list doesnt seem to work.

After spending a lot of time investigating this issue I finally found a solution. The solution is listed below:
 

string newPage = string.Empty;
Type cmType = typeof(SPMeeting);
SPMeeting mtg = (SPMeeting)
Activator.CreateInstance(cmType,
BindingFlags.NonPublic | BindingFlags.Instance,
null, null, System.Globalization.CultureInfo.CurrentCulture,
null);
Type t = mtg.GetType();
t.InvokeMember(
"m_Web", BindingFlags.NonPublic
|
BindingFlags.Instance | BindingFlags.SetField,
null, mtg, new Object[] { web });
mtg.AddPage(
"JunkYard", 1, out newPage);

The SPMeeting class has a method “AddPage”. This class has no public constructors therefore you cannot instantiate it the normal way. Once you instantiate it you need to set, via reflection, m_Web which is a private member and holds the SPWeb object. The final step is to call the AddPage method passing it the name of the page you want to add, an instanceid (not sure what this is), and an out string parameter.

SharePoint issues and work arounds!

April 3, 2009 1 comment

Cannot set Internal Name of an SPField:

The InternalName property is read only. According to the documentation the StaticName property of an SPField should:

“Get or set the internal name of the field. “

It doesnt! We had a requirement where we needed to set the InternalName of an SPField. We tried everything! various methods, reflection you name it but to no avail!

Trouble with getting SPField object after programmatically adding it:

There are a few ways you can add an SPField to a List.:

  1. SPField.Add() has three overloads
  2. SPField.CreateNewField()
  3. AddFieldAsXml() has 2 overloads

Using SPField.Add you can add an SPField by providing the DisplayName, Type and a bool specifying whether the field is required or optional. But how do you get a reference to the SPField you just added when you use this method?

You could use:

  1. SPField[int index] but you dont know the index
  2. SPField[Guid id] but you dont know the Guid
  3. SPField[string DisplayName] you do know the DisplayName but what if there is another field with the same DisplayName since there is no restriction on having more than 1 fields with the same DisplayName?
  4. SPField.GetField(string displayName) same problem as above
  5. SPField.GetFieldByInternalName(string internalName) but we dont know the internal name (linked with the issue mentioned above. This is why we needed to be able to set the InternalName).

If there was a GetFieldByStaticName it could have solved our problem. One of the overloads of the SPField.Add() method takes an SPField. We thought we would instantiate and setup our SPField object, pass it to that method to add to the List and we will then have a proper reference of our newly added SPField object right? No wrong! The SPField object is just a means to send in the data required to set up the new SPField.

It would be useful to have a method that will add an SPField and return an SPField object representing the SPField you just added. However in the absence of this there is a way around this whole issue. The way around it is to use the AddFieldAsXml() method. It takes a string argument representing the CAML for the SPField you are trying to add. In that you can specify the Guid you want for your new field. Later you can retrieve your field by using SPField[Guid id].

You can find the solution here.

Basic issue all over the place in the SharePoint Object Model:

You are adding an SPList programmatically. You want to find out whether an SPList with that name already exists. What do you do? You could try:

  1. SPList[string listName]
  2. SPWeb.GetList(string listName)
  3. SPWeb.GetListFromUrl(string pageUrl)

If the list exists it will get you the SPList object representing that list. If it doesnt it throws an exception! So if you dont have a try catch block when checking if a List with a particular name already exists your application will crash!

I think there is a need for a method which will return a particular value based on whether an SPList with that name exists (could return a bool, or the SPList object if it exists or null if it doesnt).

When adding an SPList you need to check whether an SPList with the same name exists or not because if it does then the SPList.Add() method will throw an exception. In other words it all works perfectly if you add a List and by chance the name is unique. If it is not unique then an error will be thrown and must be caught to prevent your application from crashing.  Surely, you should be able to check whether a list exists without having to catch an error!

This issue manifests itself in a lot of places in the object model. You have the same problem when trying to add an SPField, SPView e.t.c.

While programmatically adding a ListViewWebPart you cannot set the ToolbarType property:

There is a property called ToolbarType on a ListViewWebPart which is read only. There is a way around this issue which involves using reflection.

The work around for this is discussed here.

No clear way of adding Tabs to a Meeting Workspace Site:

When you create a site from the OTB Meeting workspace site definitions you are able to add pages to the site. The pages are stored in a hidden list called Workspace pages. When you add a page it adds a tab for that page at the top. However adding a WebPart page to this list programmatically doesnt solve the problem as it will not add the tab. There is a very painful way around this and it involves Reflection!

The solution is here.

Adding a WebPartPage to an SPList sub folder:

There is a way of adding it to the root folder but there doesnt seem to be a direct way of adding it to a sub folder. Again there is a way around this. You add the WebPartPage to the root folder of the SPList and then move it to the sub folder you actually wanted to add it to.

You can find a solution for this here.

Cannot get WebParts from a MySite (SPS 2003):

Not sure if its the same on MOSS 2007 but when we tried to get WebParts from the default page on a Personal Site (unless its your Personal Site) you wont be able to get an instance of the WebParts that exist on the page. Raised this issue with Microsoft who said this was for security reasons. However no such restrictions when trying to get data from the lists on the Personal Site 🙂 Run with elevated priviledges makes no difference either. The only way around this is to impersonate the user whose Personal Site it is or just give up!

.NET WebControl: TextBox with counter

April 3, 2009 Leave a comment

Came across a need for a TextBox control with a countdown counter. Since I couldnt find one that would fit my requirements I decided to write one up.

It is in a very basic format at the moment and there is quite a lot that can be done with it. Feel free to improve, enhance, extend it.

Here is a screen shot of what it looks like:

TextBox with a countdown counter.

It is a very handy control to have.  You can use it in all three of the TextBox mode’s i.e. SingleLine, MultiLine, Password.

You can get the control and source code from here:

http://textboxwithcounter.codeplex.com/

Below is an example of how you can use this WebControl:

protected void Page_Load(object sender, EventArgs e)
{
    TextBoxWithCounter tbwCounter =  
    new TextBoxWithCounter(200, 5, 25, TextBoxMode.MultiLine);
    this.Form.Controls.Add(tbWcounter);
}