Monday, November 12, 2007

Getting the url of the list

Hello!

I was faced with a small problem the other day. In an external database the url to a content item, such as a document, discussion thread or a wiki page, was to be stored in several parts; the url to site, the url to the list and finally the url to the list item.
The easy part was the site and the item urls since the come out of the SPItemEventProperty of the event handler I was working in. But when it came to the list, it was something different. I.e. the user can change the Title of the list and therefore the title cannot be used. There are several ways of doing this, but the way I choose was this method;
First, get the value of the field named "Encoded Absolute URL" which out of the SPItemEventProperty.ListItem. This url is the exact url SharePoint will use when a user click on a link for that item.
Decoding and encoding this url is necessary for what I did next, taking the substring of this url and removing the site url and the item url will get you the url for the list.
Below is a code example of what I did;

                /* First, encode the property from the event handler */
                string siteUrl = SPEncode.UrlEncodeAsUrl(properties.WebUrl);

                /* Next, get the absolute url from the ListItem property and decode at the same time*/
                string absoluteItemUrl = SPEncode.UrlDecodeAsUrl(properties.ListItem["Encoded Absolute URL"].ToString());

                /* Encode the url to get the absolute url in the same encoding as the web url */
                absoluteItemUrl = SPEncode.UrlEncodeAsUrl(absoluteItemUrl);

                /* Next, get the substrings to leave only the list url */

                string temp = absoluteItemUrl.Substring(siteUrl.Length);
                string tempUrl = temp.Substring(0, temp.LastIndexOf('/') + 1);

Posted by Zeb at 16:05:36 | Permanent Link | Comments (0) |

Saturday, September 29, 2007

Using PeopleEditor as a Template Field

Hi!

When a customer of mine wanted a web part and described how it was supposed to work, there was a need to use the PeopleEditor class and use it insida a template field in a SPGridView. There are a lot of ways to accomplish the following task, but I choose to use a DataTable as the datasource for the gridview.

The SPGridview has four columns, a generated edit-column and three template fields. One of the template fields consists of a Literal control when displaying the item and a PeopleEditor when in editing the item. The problem was to set the default value of the PeopleEditor to a value that resides at a specific location in a specific list. The value in the list is of the type Person.

Trying to use the PeopleEditor.Accounts.Add() methos did not work and after closely looking the the SDK for the PeopleEditor class, there can be seen that the method CommaSeperatedAccounts property could be set. And after putting the value from the list (the Person column) into the PeopleEditor.CommaSeperatedAccount property the template field was showing the default value from the item view in the edit view of the SPGridview.

The documentation of the PeopleEditor is very shortcoming and perhaps this will be of some use to someone.

//Sebastian

Posted by Zeb at 18:29:10 | Permanent Link | Comments (4) |

Wednesday, September 19, 2007

Using KPI-lists with advanced filtering

Hi!

A customer of mine wanted a KPI list with values originating from a SharePoint list which already exists. The customer already have a KPI list with a few dozens of indicators which were quite easy to make. However, this new KPI list was to be using a more advanced filter for the SharePoint list.

The underlying list have several column where one of these columns contains persons. The indicators in the KPI list are to be made for list items where person column only has values and then grouped together with another column. The main problem here is to make the view where only the desirable list items is shown. When trying to make the indicators from the list one will discover that there is no way of getting only the list items where the person column contains some value. Hence a try in SharePoint Designer was made. Here the new view of the list could be extracted, but only after the listview web part had been converted into a Data List Web Part and the KPI list indicators didn't seem to like that since I didn't get it to work.

The solution was to make a small console app that changes the view of interest by modifying the Query property of the SPView object with a caml query like this one:

<Where><IsNotNull><FieldRef Name='Column_Name' /></IsNotNull></Where>

After updating the view and going back to the browser, the view have changed and only displaying the list items where the column, in the example above named Column_Name, have values.

There are many other syntaxes that can be used with caml queries and Patrick Tisseghem has made a very useful application called CamlBuilder.

Hope this help someone.

//Sebastian

Posted by Zeb at 21:33:40 | Permanent Link | Comments (1) |

Thursday, September 13, 2007

Timezones and SharePoint

Hi!

A customer of mine wanted to make sure that users in different time zones always has the local time shown when looking at items in document libraries, lists etc. Beforehand we knew that SharePoint sites have their own regional settings, independent of the SharePoint server regional settings. But this was not enough. Let's say that an project site has members that is scattered all over the world, how is the regional settings supposed to be configured?
The answer was easier than expected. None of us knew that, at least in SharePoint 2007, each users can have their own regional settings. The regional setting is reached if clicking on the "Welcome <loginname>"-dropdown in the top right corner of SharePoint. Select My Settings and you are redirected to a page with a toolbar that looks like this:

 

Here the user can change her/his own regional settings by clicking on the My Regional Settings link. Here the user have the choice of either keeping the regional settings of the web site or making her/his own regional settings.

 

The effect of this change is immidiate.
It might be a good idea to make a small javascript that compares the local time zone with the timezone in SharePoint and if there is a difference pop up a message that says something like, don't forget to change your timezone. This is only a precation since it is quite easy not to remeber to change the regional settings in SharePoint after setting the clients timezone.

//Sebastian

Posted by Zeb at 14:58:16 | Permanent Link | Comments (2) |

Thursday, August 30, 2007

Running event handlers

Hi!

I was presented with a problem while working at a customer of mine. When running event handlers, specially ItemUpdated, the code might be run several times by different threads. The thing is that the eventhandler makes some changes to the list item that generates an error if the code is run again. That is to say that the name of the item changes in the eventhandler. The problem consists of making sure that the code is only run once.

To solve this problem, I used a hashtable to store a temporary variable. Using a hashtable has the advantage that once the variable is set in the hastable, a try/catch loop will sort out the following threads since the hastable will generate an error if trying to insert duplicates.

Thus, when an error is received from the hashtable, a return statement is set, allowing only the first thread to run the code. Once the code is run, don't forget to reset the hashtable.

Since Gustaf, a colluege of mine, come up with the original idea, here is a link to his blog, also with code examples. http://gustafwesterlund.blogspot.com/2007/08/how-to-stop-infinite-recursionsloops-in.html

//Sebastian

Posted by Zeb at 14:24:47 | Permanent Link | Comments (0) |

Tuesday, August 21, 2007

Dynamically dropdown lists in SharePoint

Hi again!

This article will be about making dynamically dropdown lists in SharePoint. By dynamically dropdown lists I mean that depending on the selected value from one list, the second list is populated with different values.

Most of the code below has been made available by Gustaf Westerlund, an associate of mine. Many thanks!

A customer of mine asked for this feature in an edit form for document properties in SharePoint and the solution was to first create a new custom edit form for the document library of interest, see article about creating a custom form, and then to modify this new edit form so that the desired functionality is obtained.

The prerequisites for this example is that the document library of interest has two columns of type choice and is presented as dropdown lists. The first column has fixed values that can be choosen between and the second dropdown should be depending on the value from the first dropdown.

 When the new form is created and the DataListWebPart has been inserted, a javascript is inserted into the new form to make two dropdownlists dependent on each other. This can only be done after the ListFormWebPart has been removed and the DataListWebPart has been inserted. When the new web part is inserted, the controls are visible for editing in an ordinary table.

The two dropdown lists from the columns mentioned above can now be accessed since the id for the controls can be obtained, either by scrolling though the html-code for the edit form or by running IE Developer Toolbar, downloadable from Microsoft.

Now, I used SharePoint Designer for this part but any editor should be fine, insert a javascript into the edit form. The location of the javascript has some importance and remember that the control that is being called must have been defined before the javascript tag is inserted. I put the script tag just outside the table of the DataListWebPart but still inside the main content asp tag.

<script type="text/javascript">

</script>

The idea now is to populate the second dropdown list with values when the first dropdownlist changes. To do this, a function in the javascript is associated with the onchange event of the first dropdown list. This part of the script is done as shown below.

<script type="text/javascript">                       

var Loader = function()

                             {                               document.getElementById("ctl00_m_g_99ab9b11_70a4_4341_80f3_88f3266dde73_ff11_1_ctl00_DropDownChoice").attachEvent("onchange", ProcessOnChange);

                             }

                             Loader();

</script>

For everyone good at javascript, this is probably a piece of cake, but for many others, including myself, this requires some explanation.

The function Loader only has one row, which finds the control with id ctl00_m_g_99ab9b11_70a4_4341_80f3_88f3266dde73_ff11_1_ctl00_DropDownChoice, which can be found from the form by the methods mentioned above.  The attachEvent method states that on a change of the dropdownlist the function ProcessOnChange should be run. This function is not yet implemented. Once the page is loaded the Loader function is fired, making the onChange event association to the first dropdown list.

The ProcessOnChange we made looking like this;

var ProcessOnChange = function()

                             {

                                                          clearAll();

                                                          populateDropdown ();           

                             }

The functions clearAll is run to make sure that the second dropdown list is empty before new values are populated into the list.

clearAll is defined as below;                        

var clearAll = function()

                             {

                                                          var dropdownlist = document.getElementById("ctl00_m_g_99ab9b11_70a4_4341_80f3_88f3266dde73_ff8_1_ctl00_DropDownChoice");

                                                          var len = dropdownlist.length;

                                                          for (i = 0; i < len; i++)

                                                          {

                                                              dropdownlist.remove(0);

                                                          }

                             }

Here the id for the getElementById is the id for the second dropdown list. It can be obtained by the methods mentioned above.

Lastly, the populateDropdown function is to be defined. This function is separated into three sections for easier understanding. The first part is simply the connection to the two different dropdown lists.

 

var dropdownProcess = document.getElementById("ctl00_m_g_99ab9b11_70a4_4341_80f3_88f3266dde73_ff11_1_ctl00_DropDownChoice");

var dropdownDocumentType = document.getElementById("ctl00_m_g_99ab9b11_70a4_4341_80f3_88f3266dde73_ff8_1_ctl00_DropDownChoice");

 

The second part is the definition of different variables in order to more efficiently populate the second dropdown list. Here only three options are defined, but many more options can be made available for inserting into the dropdown list.

 

var documentTypeAuditPlan = document.createElement('option');

documentTypeAuditPlan.text = "Audit Plan";

var documentTypeAuditReport = document.createElement('option');

documentTypeAuditReport.text = "Audit Report";                                          

var documentTypeAuditActionPlan = document.createElement('option');

documentTypeAuditActionPlan.text = "Audit Action Plan";                                                       

 

The dropdown list requires variables defined as above.

The last part of the function is a case statement, in this case a switch is used. In order to get the switch to work, the evaluated statement must be placed in a variable.

var selectedProcess = dropdownProcess.options[dropdownProcess.selectedIndex].text;                                         

switch(selectedProcess)

{

case "Management Process":

dropdownDocumentType.add(documentTypeAuditPlan);                                                                                       dropdownDocumentType.add(documentTypeAuditReport);                                                    dropdownDocumentType.add(documentTypeAuditActionPlan);                                           

break;               

default:

}

 

This switch will look at the text of the selected value from the first dropdown list and if the text is Management Process, which is one of the choices for that column to which the dropdown list is linked, the second dropdown list will be populated with the values Audit Plan, Audit Report and Audit Action Plan.

Now, the values that the second dropdown list is being populated with must also exist in the choice list linked to the dropdown list. Otherwise if a value that is not in the choice list is being selected, SharePoint will return an error.

I'm not a big fan of JavaScript because of several reasons and everyone taking this article as a guideline should be aware of that it is quite tricky to get the JavaScript to function properly. Agood way of trying your scripts before making them part of the edit form is to go to w3schools and try their try-it Editor. Just paste your code and click the button to see if it works.

Another thing that might help is to try the code step by step and write in a couple of alerts(string s), as a simple debugger, that will pop up a string in a dialog box when it is run.

Good luck!

//Sebastian

Posted by Zeb at 19:42:41 | Permanent Link | Comments (2) |

Custom Forms in SharePoint 2007

Hi!

This post will be about creating a custom form for a list. The need to do this arose when a customer of mine wanted a pair of dynamic dropdown lists where this was the first step in the solution.

This information has been taken from SharePointExpert and from Kristian Kalsing's blogpost about custom forms which are both excellent blogs with many good articles.

First off, open SharePoint Designer (SPD) and open the web site of interest.

Next, navigate to the list which is to have the custom form and find the folder Forms inside the list. Inside this folder resides all the default forms that the library will use for any given situation such as uploading, checking in or editing the properties of a document.

Now, open the form that you whish to make a customized version of. Notice that the form only contains a single webpart, the ListFormWebPart.

This webpart does not allow us to do anything useful and therefore we are going to remove it. But before you do this, make a copy of the form you opened and give it a good name. In my case, with my customer, it was the EditForm.aspx that was copied to EditForm2.aspx.

When the ListFormWebPart has been removed, a new control is going to be inserted. Goto Insert -> SharePoint Control -> Custom List Form. This will bring up a dialog box where the list, content type and operation type can be selected. Once you have selected all of these, click the Ok button.

As can be seen, I have chosen to make a new Edit form for my Shared Document which uses the Document content type. Now, all metadata fields will appear in a table which you can edit and modify as you whish. Be careful when removing fields that you don't want the users to see so that the fields are not required.

When you are done, go to a browser and navigate to the list that has the customized form and try it out. This can be done in many ways, the easiest that I have found is to go to the original form and then write in your name instead of the original form name in the adress field.

After this comes the tricky part, getting the form to actually appear as the default form for the operation.

To be able to do this go to the list in SharePoint Designer and right click. Choose Properties and then go to the tab named Supporting Files. Here you will be able to upload different forms than the default SharePoint forms, such as the form you have just created.

Upload you new form. This is not end of it though. When hitting the Ok button the error "the page you have selected does not contain a reference to the correct sharepoint list..." will appear. To get rid of this error you have to go back to the original form and copy the whole WebPartZone tag with all its content to the new form. This will give you two web part zones in your form and both have the same Id. Rename your webpart zone with the DataListWebPart to something else.

Now go down into the ListFormWebPart, that is the web part you just copied into your customized form, and set the IsVisible tag to false.

Now the new form will be the default form for the specified action.

Good luck!

 

//Sebastian

Posted by Zeb at 17:27:06 | Permanent Link | Comments (9) |

Thursday, June 21, 2007

Creating Revision handling in SharePoint

The following scenario has been given; A document handling system is to be created, where a global document id together with version management and revision mangement is to be implemented.

The hardest part of the above mentioned topics was the revision handling. For all of you out there that does not have much experience with document mangement here is a brief summary.

In SharePoint 2007, document management has been reactivated (it was removed from SharePoint 2003). The functionality that comes out-of-the-box (OOB) is quite good and wil suffice in most situations, but for more complex situations some development will have to take place. OOB comes version management which allows for very powerful document version management such as minor (drafts) and major (published) versions, check-in and check-out of documents are also available. Content approval is a very good way to have your documents under control and knowing what is in your document center. However, the functionality of revision and global document id's is still not implemented OOB and must therefore be developed.

In the case listed above, the global document id was quite easy to implement as a new meta-data column in the document library connected to a eventhandler and a small database. The revision management was quite the opposite. Revision is a good way of having documents for each version of a product, where the old product manual must still exist in the library. The idea of the revision is to copy the file to have the same metadata as the previous file and then change the Revision, for example from A to B. After this is done, since the document requires a check-out before editing the properties, the source document are to be restored to the previous published version. This is the tricky part. The document library settings says that five minor and five major versions are stored, but when the file is accessed from the object model, there is only one version of the file available from the SPFileVersionCollection...

The solution for the moment is to manually discard the check-in, but it is not a good solution. I'll let you know as soon as possible if I find a solution to this problem.

 //Sebastian

Posted by Zeb at 10:13:15 | Permanent Link | Comments (0) |

Tuesday, April 10, 2007

SharePoint och Exchange Forum

Do not miss Sweden's biggest SharePoint and Exchange Forum. This year the forum will be held at Utkiken, Nynäshamn. For more information, visit www.seforum.se (Swedish web page).

//Sebastian

Posted by Zeb at 09:46:27 | Permanent Link | Comments (0) |

Wednesday, March 28, 2007

Event handler in Document Library

When working with event handlers in SharePoint, the event handlers will usually be associated with different lists. Using lists like a Task list or a Custom list and using the item adding event will present the developer with the SPItemEventProperty object as an argument. This object holds the information a developer can work with such as the ListItem property which will return the SPListItem that fired the event in question.

A problem arose when I tried making an event handler for a document library. Overriding the ItemAdding method, thus gaining access to the SPItemEventProperty, I found that this object returns a null exception. When debugging the event handler I saw that the SPItemEventProperty was in fact null. This is quite interesting since the event handlers are working on other list types.

After a few hours looking for information about this problem, non had come up with a sloution. One way to get around the problem should be to directly adress the column of interest in the listitem, like such:

  property.ListItem[columnName]

However, this I did not get to work either, still getting the nullException when running the event.

This issue  is still under investigation. If anyone has an answer, please post a repsonse.

Best regards

Sebastian

Posted by Zeb at 17:42:54 | Permanent Link | Comments (0) |