<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>I.NFECTIO.US</title>
    <link>http://i.nfectio.us</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>"Nothing in life is worth doing if you have no life while doing it"</description>
    <item>
      <title>Working with the PeoplePicker Data Values in SharePoint</title>
      <description>The SharePoint PeoplePicker control is great for selecting a user, but it doesn't do much more than that. When it stores the value in the database, like many other controls, it uses the familiar ;# delimiter like so: 15;#Robert Silva. The first value is the ID of the user in the User Information List in the top-level site. It's a hidden list. If you want to see what's in it, use this cool little PowerShell trick I just learned myself:

&lt;pre&gt;
# Load the Microsoft.SharePoint.dll assembly
[System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")

# Load your site
$site = New-Object Microsoft.SharePoint.SPSite("http://url")

# Load the User Information List
$userlist = $site.RootWeb.Lists["User Information List"]

# Print the ID and Title of each list item
$userlist.Items | Format-List -property ID, Title

$site.RootWeb.Dispose()
$site.Dispose()
&lt;/pre&gt;

This information itself doesn't do you much good. What if you want the email address of the user or their user profile. Luckily, SharePoint provides everything we need and makes it (finally) pretty easy for us. In the following code example, I have a column named Technician defined as a Person or Group column. In code, I use this list item value to load a User and their User Profile. The magic is the SPFieldUserValue object that we pass the 15;#Robert Silva into. Using its User property, we are able to do whatever we want with them.

&lt;pre&gt;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SPSite site = new SPSite("http://url"))
            {
                using (SPWeb web = site.AllWebs["webname"])
                {
                    SPList list = web.Lists["listname"];
                    ServerContext sc = ServerContext.GetContext(site);
                    UserProfileManager profileManager = new UserProfileManager(sc);
                    foreach (SPListItem item in list.Items)
                    {
                        SPFieldUserValue uv = new SPFieldUserValue(web, item["Technician"].ToString());
                        if (profileManager.UserExists(uv.User.LoginName))
                        {
                            UserProfile user = profileManager.GetUserProfile(uv.User.LoginName);                            
                        }
                    }
                }                
            }

            System.Console.ReadLine();
        }
    }
}

&lt;/pre&gt;</description>
      <pubDate>Sun, 28 Oct 2007 21:54:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:d8ec9c31-41e0-4e99-b78a-4b8eca10c5f4</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2007/10/28/working-with-the-peoplepicker-data-values-in-sharepoint</link>
      <category>SharePoint</category>
      <category>SharePoint</category>
    </item>
    <item>
      <title>Create Dynamic InfoPath Forms Based on User Input</title>
      <description>&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To build a form that represents my companies timesheets which currently vary between 4 and 5 week long pay periods. So I need the user to be able to select the current pay period and based on a set of start and end dates, display a form that allows them to fill in their time for that period. Lets take a look at what we want to accomplish:&lt;/p&gt;
&lt;caption&gt;4-week Time Period&lt;/caption&gt;&lt;br /&gt;
&lt;img src="http://i.nfectio.us/images/Timesheet4.png" /&gt;
&lt;br /&gt;
&lt;caption&gt;5-week Time Period&lt;/caption&gt;&lt;br /&gt;
&lt;img src="http://i.nfectio.us/images/Timesheet5.png" /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since InfoPath forms are simply displaying the underlying XML datasource using XSLT, by changing the XML datasource structure, we can change the representation of the form dynamically. In the image below, you can see that the design does not specify the individual weeks and days. Instead, I use a Repeating Section control to represent a Week and a Repeating Table to represent a Day. By cloning these nodes for each week and day in the pay period, we are able to generate our form. I'll let the code speak for itself on this one as it took me quite awhile to figure it out myself.&lt;/p&gt;
&lt;img src="http://i.nfectio.us/images/FormDesignSmall.png" /&gt;
&lt;br clear="all" /&gt;
&lt;img src="http://i.nfectio.us/images/PayPeriods.png" align="left" /&gt;&lt;img src="http://i.nfectio.us/images/MainDataSource.png" align="right" /&gt;&lt;br /&gt;
&lt;br clear="all" /&gt;

&lt;pre&gt;
using Microsoft.Office.InfoPath;
using System;
using System.Xml;
using System.Xml.XPath;


namespace Timesheets___Contract
{
    public partial class FormCode
    {
        /// &lt;summary&gt;
        /// Stores the initial schema to allow a quick reset
        /// when user changes time period.
        /// &lt;/summary&gt;
        private object InitialSchema
        {
             get { return FormState["InitialSchema"]; }
             set { FormState["InitialSchema"] = value; }
        }
        private object FirstLoad
        {
            get { return FormState["FirstLoad"]; }
            set { FormState["FirstLoad"] = value; }
        }
        private object SaveLocation
        {
            get { return FormState["SaveLocation"]; }
        }
        
        // NOTE: The following procedure is required by Microsoft Office InfoPath.
        // It can be modified using Microsoft Office InfoPath.
        public void InternalStartup()
        {
            EventManager.FormEvents.Loading += new LoadingEventHandler(FormEvents_Loading);
            EventManager.XmlEvents["/my:TimesheetData/my:period"].Changed += new XmlChangedEventHandler(period_Changed);
            EventManager.FormEvents.Submit += new SubmitEventHandler(FormEvents_Submit);            
        }

        public void FormEvents_Loading(object sender, LoadingEventArgs e)
        {
            // Grab the root of the Main Data Source
            XPathNavigator xpnRoot = this.MainDataSource.CreateNavigator();
            
            // Set some form state values
            FormState.Add("InitialSchema", xpnRoot.SelectSingleNode("/my:TimesheetData/my:weeks", NamespaceManager).InnerXml);
            FormState.Add("FirstLoad", false);

            // Determine the SaveLocation
            string strUri;
            if (Application.Environment.IsBrowser)
            {
                strUri = e.InputParameters["SaveLocation"].ToString();
            }
            else
            {
                strUri = this.Template.Uri.ToString();
            }
            FormState.Add("SaveLocation", strUri);

            // If this is the first time the form is opened, calculate the current period
            XPathNavigator xpnPeriod = xpnRoot.SelectSingleNode("/my:TimesheetData/my:period", NamespaceManager);
            if (string.IsNullOrEmpty(xpnPeriod.Value))
            {
                // Set a state variable to determine FirstLoad
                this.FirstLoad = true;
                this.CalculateAndSetCurrentPeriod(xpnRoot);
            }
        }

        /// &amp;lt;summary&gt;
        /// Iterates the Time Periods from the SharePoint List
        /// Data Source and determines the current pay period.
        /// Sets the value of the pay period drop-down which fires
        /// the Changed event to build the form.
        /// &amp;lt;/summary&gt;
        /// &amp;lt;param name="root"&gt;&amp;lt;/param&gt;
        /// &amp;lt;param name="periodsRoot"&gt;&amp;lt;/param&gt;
        private void CalculateAndSetCurrentPeriod(XPathNavigator xpnRoot)
        {
            DateTime today = DateTime.Now;

            // Grab the root of the TimesheetPeriods Data Source
            XPathNavigator xpnTPRoot = DataSources["TimesheetPeriods"].CreateNavigator();

            // Loop the periods looking for the one this date falls in
            XPathNodeIterator xpniPeriods = xpnTPRoot.Select("/dfs:myFields/dfs:dataFields/dfs:Payroll_Periods", NamespaceManager);
            while (xpniPeriods.MoveNext())
            {
                DateTime dtStartingDate = DateTime.Parse(xpniPeriods.Current.SelectSingleNode("@Starting_Date", NamespaceManager).Value);
                DateTime dtEndingDate = DateTime.Parse(xpniPeriods.Current.SelectSingleNode("@Ending_Date", NamespaceManager).Value);

                if (today &gt;= dtStartingDate &amp;&amp; today &lt;= dtEndingDate)
                {
                    // Set the Period dropdown to the current period
                    XPathNavigator xpnPeriod = xpnRoot.SelectSingleNode("/my:TimesheetData/my:period", NamespaceManager);
                    xpnPeriod.SetValue(xpniPeriods.Current.SelectSingleNode("@Payroll_Period", NamespaceManager).Value);
                    break;
                }
            }
        }

        /// &amp;lt;summary&gt;
        /// Creates a DateTime array of values between 2 dates.
        /// &amp;lt;/summary&gt;
        /// &amp;lt;param name="startingDate"&gt;&amp;lt;/param&gt;
        /// &amp;lt;param name="endingDate"&gt;&amp;lt;/param&gt;
        /// &amp;lt;returns&gt;&amp;lt;/returns&gt;
        private DateTime[] FillDays(DateTime dtStartingDate, DateTime dtEndingDate)
        {
            TimeSpan tsDaySpan = dtEndingDate - dtStartingDate;
            // Add 1 day to span to accomodate for 00:00:00 time value.
            int iNumDays = System.Math.Abs(tsDaySpan.Days) + 1;
            DateTime[] dtRange = new DateTime[iNumDays];
            
            int i = 0;
            while (i &lt; iNumDays)
            {
                dtRange[i] = dtStartingDate.AddDays(i);
                i++;
            }
            return dtRange;
        }

        private void SetNilValue(XPathNavigator xpnNode, string strValue)
        {
            if (xpnNode.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance"))
            {
                xpnNode.DeleteSelf();
            }
            xpnNode.SetValue(strValue);
        }

        private void BuildTimesheet(DateTime dtStartingDate, DateTime dtEndingDate)
        {
            this.ResetTimesheet();

            // Calculate the date range
            DateTime[] dtRange = this.FillDays(dtStartingDate, dtEndingDate);

            XPathNavigator xpnRoot = this.MainDataSource.CreateNavigator();
            // Set some initial node elements
            XPathNavigator xpnWeeks = xpnRoot.SelectSingleNode("/my:TimesheetData/my:weeks", NamespaceManager);
            XPathNavigator xpnWeek = xpnWeeks.SelectSingleNode("my:week", NamespaceManager);
            XPathNavigator xpnDay;

            foreach (DateTime dtDay in dtRange)
            {
                string strDayOfWeek = ((int)dtDay.DayOfWeek + 1).ToString();
                xpnDay = xpnWeek.SelectSingleNode("my:days/my:day[" + strDayOfWeek + "]", NamespaceManager);
                if (xpnDay == null)
                {
                        XPathNavigator _xpnDays = xpnWeek.SelectSingleNode("my:days", NamespaceManager);
                        XPathNavigator _xpnDay = _xpnDays.SelectSingleNode("my:day[1]", NamespaceManager);
                        _xpnDays.AppendChild(_xpnDay);
                        xpnDay = xpnWeek.SelectSingleNode("my:days/my:day[" + strDayOfWeek + "]", NamespaceManager);
                }
                // Fill in the Date using "Jan 13" format
                XPathNavigator xpnDate = xpnDay.SelectSingleNode("my:date", NamespaceManager);
                this.SetNilValue(xpnDate, String.Format("{0} {1}", dtDay.ToString("MMM"), dtDay.ToString("%d")));
                XPathNavigator xpnDayOfWeekAttr = xpnDate.SelectSingleNode("@my:dayofweek", NamespaceManager);
                xpnDayOfWeekAttr.SetValue(strDayOfWeek);
                
                // New weeks start on Sunday (except for the last week)
                if (dtDay.DayOfWeek == DayOfWeek.Saturday &amp;&amp; dtDay != dtEndingDate)
                {
                    XPathNavigator _xpnWeek = xpnWeeks.SelectSingleNode("my:week[1]", NamespaceManager);
                    xpnWeeks.AppendChild(_xpnWeek);
                    xpnWeek = xpnWeeks.SelectSingleNode("my:week[last()]", NamespaceManager);
                }
            }

        }

        /// &amp;lt;summary&gt;
        /// Resets the timesheet to the default schema saved during form load.
        /// &amp;lt;/summary&gt;
        private void ResetTimesheet()
        {
            XPathNavigator xpnRoot = this.MainDataSource.CreateNavigator();
            xpnRoot.SelectSingleNode("/my:TimesheetData/my:weeks", NamespaceManager).InnerXml = this.InitialSchema.ToString();
        }

        /// &amp;lt;summary&gt;
        /// If this is the first time the form is loaded, forces the timesheet
        /// to rebuild and redisplay when a new period is selected.
        /// &amp;lt;/summary&gt;
        /// &amp;lt;param name="sender"&gt;&amp;lt;/param&gt;
        /// &amp;lt;param name="e"&gt;&amp;lt;/param&gt;
        public void period_Changed(object sender, XmlEventArgs e)
        {
            if ((bool)this.FirstLoad)
            {
                string strPeriod = e.Site.Value;
                XPathNavigator xpnTPRoot = this.DataSources["TimesheetPeriods"].CreateNavigator();
                XPathNavigator xpnPeriod = xpnTPRoot.SelectSingleNode("/dfs:myFields/dfs:dataFields/dfs:Payroll_Periods[@Payroll_Period=\"" + strPeriod + "\"]", NamespaceManager);
                DateTime dtStartingDate = DateTime.Parse(xpnPeriod.SelectSingleNode("@Starting_Date", NamespaceManager).Value);
                DateTime dtEndingDate = DateTime.Parse(xpnPeriod.SelectSingleNode("@Ending_Date", NamespaceManager).Value);

                this.BuildTimesheet(dtStartingDate, dtEndingDate);
            }
        }

        /// &amp;lt;summary&gt;
        /// Submit the form to the SharePoint Timesheets library. Place the form
        /// in a folder equal to the name of the current period.
        /// &amp;lt;/summary&gt;
        /// &amp;lt;param name="sender"&gt;&amp;lt;/param&gt;
        /// &amp;lt;param name="e"&gt;&amp;lt;/param&gt;
        public void FormEvents_Submit(object sender, SubmitEventArgs e)
        {
            // Get the XPN for the Period node
            XPathNavigator xpnPeriod = this.MainDataSource.CreateNavigator().SelectSingleNode("/my:TimesheetData/my:period", NamespaceManager);
            
            // Set the Submit Folder to the proper Period subfolder of the Form Library
            FileSubmitConnection fscMain = (FileSubmitConnection)this.DataConnections["Main submit"];
            fscMain.FolderUrl = this.SaveLocation + "/" + xpnPeriod.Value;

            // Execute the submit connection
            try
            {
                fscMain.Execute();
                e.CancelableArgs.Cancel = false;
            }
            catch (Exception ex)
            {
                e.CancelableArgs.Cancel = true;
            }
        }
    }
}

&lt;/pre&gt;


</description>
      <pubDate>Sun, 28 Oct 2007 19:59:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:b6d58f71-8aea-44f4-810a-3e4cb6414f6b</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2007/10/28/create-dynamic-infopath-forms-based-on-user-input</link>
      <category>SharePoint</category>
      <category>SharePoint</category>
      <category>InfoPath</category>
    </item>
    <item>
      <title>Sortable IP Addresses in SharePoint</title>
      <description>&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An IP Address in its dotted-quad format will not sort properly. For example, 192.0.0.0 will show before 69.0.0.0 when sorted in ascending order (1 comes before 6 right?). So we need a way to have SharePoint sort on this column.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To properly sort an IP address, you must first convert it into an integer. Doing this converts 192.168.0.1 into 3232235521 and 69.0.0.0 into 1157627904. Now when you sort the IP, it will correctly order them based on the value of the integer. To accomplish this in SharePoint, you have two options and I will cover both of them. The first is to create a hidden calculated field on your list and use a formula to convert the IP into an integer. Then, by creating views that sort by the hidden field, you can display the sorted IPs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Create a calculated field to sort IPs&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create a new field named IPAddress and leave it as a single line of text. Add another field named SortableIPAddress and make it a calculated field. Now add the following formula (&lt;font color="#ff0000"&gt;warning&lt;/font&gt;: its not pretty).&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;=VALUE(IF(NOT(ISBLANK([IPAddress])),(LEFT([IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],1)-1)*16777216)+(MID([IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1,FIND(&amp;quot;.&amp;quot;,[IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1)-FIND(&amp;quot;.&amp;quot;,[IPAddress],1)-1)*65536)+(MID([IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1)+1,
FIND(&amp;quot;.&amp;quot;,[IPAddress],FIND(&amp;quot;.&amp;quot;,[IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1)+1)-FIND(&amp;quot;.&amp;quot;,[IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1)-1)*256)+(RIGHT([IPAddress],
LEN([IPAddress])-FIND(&amp;quot;.&amp;quot;,[IPAddress],
FIND(&amp;quot;.&amp;quot;,[IPAddress],FIND(&amp;quot;.&amp;quot;,[IPAddress],1)+1)+1))),0))&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Once you add this calculated field, create a new view that sorts on the hidden SortableIPAddress field.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 431px; height: 163px" src="http://i.nfectio.us/images/ipcft/SortByIP.png" border="0" alt="Sort By Ip View" title="Sort By Ip View" width="431" height="163" /&gt;&lt;/p&gt;
&lt;p&gt;Now lets test our work. First we add some IPs out of order and see that they are displayed in the order they were input.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 473px; height: 155px" src="http://i.nfectio.us/images/ipcft/NotSortedView.png" border="0" alt="Not Sorted" title="Not Sorted" width="473" height="155" /&gt;&lt;/p&gt;
&lt;p&gt;And when we switch to the sorted view, we can see that it orders them properly. Ordinarily, you wouldn't have the SortableIPAddress field visible in your view. It's shown here as an example.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 473px; height: 155px" src="http://i.nfectio.us/images/ipcft/SortedView.png" border="0" alt="Sorted View" title="Sorted View" width="473" height="155" /&gt;&lt;/p&gt;
&lt;p&gt;This is the easy way, being a sucker for punishment, I decided to go the hard way and create a custom field type to handle this internally. Thats next after this SharePoint Tip on field naming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SharePoint Tip #3241&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Never name anything in SharePoint with spaces during creation. After creation, edit the name and add your spaces. Then you'll never have to deal with the IP_x0020_Address problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Create a custom field type to sort IPs&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Custom field types in SharePoint are great as long as you keep them simple. The API needs a lot of refactoring to make it a robust mechanism for extending SharePoint and more complete documentation wouldn't hurt either. Here's the setup, we want a custom field that allows you to enter an IP in dotted-quad format, validate it, store it as an integer (which makes it sortable) and display it back in dotted-quad format. Easy right? Actually, it's not too difficult, but its not a perfect solution either as you'll see.&lt;/p&gt;&lt;p&gt;I'm not going to cover the details of creating a Custom Field Type project in Visual Studio. The important points for the IPAddress field type are the conversions from dotted-quad to long and the rendering of the IP in the different views. Since we want to make sure we have valid IP addresses to begin with, we'll borrow the idea and code from Todd Bleekers book (&lt;a href="http://www.amazon.com/Developers-SharePoint-Services-Platform-Programming/dp/1584505001" target="_blank" title="Developer's Guide to Windows SharePoint Services 3.0"&gt;Developer's Guide to Windows SharePoint Services 3.0&lt;/a&gt;) for a Regular Expression based validated field. By overriding the GetValidatedString method of the SPField class, we are able to validate and convert the dotted IP into it's numeric format.&lt;/p&gt;
&lt;pre&gt;
public override string GetValidatedString(object value) {
	string ipaddress = value.ToString();
	if (!string.IsNullOrEmpty(ipaddress)) {
		if (!ValidIp(ipaddress)) {
			throw new SPFieldValidationException("Invalid IP Address format detected.");
		}
		return IpToLong(ipaddress).ToString();
	}
	// We defer to base to use its Required field validation
	return base.GetValidatedString(value);
}&lt;/pre&gt;
&lt;p&gt;&lt;font size="2"&gt;And the Valid IP method just uses a regular expression to match the 0.0.0.0-255.255.255.255 range.&lt;/font&gt;&lt;/p&gt;
&lt;pre&gt;
private bool ValidIp(string ipaddress) {
	string regExStr = @"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
	Regex reg = new Regex(regExStr, RegexOptions.IgnoreCase);
	return reg.IsMatch(ipaddress);
}&lt;/pre&gt;
&lt;p&gt;These methods make sure we have a valid IP in dotted-quad format, converts it into the numeric format and ultimately stores it in the database. Now we need to reverse it when we display it to the user. SharePoint offers several methods to alter the display of the data in an SPField object. I've chosen to just override the GetFieldValueForEdit method to convert the stored value on the Edit Page and a RenderPattern for Display which we'll discuss below.&lt;/p&gt;
&lt;pre&gt;
public override string GetFieldValueForEdit(object value) {
	string ShouldBeIP = value as String;
	if (!string.IsNullOrEmpty(ShouldBeIP)) {
		return LongToIp(ShouldBeIP);
	}
	return ShouldBeIP;
}&lt;/pre&gt;
&lt;p&gt;At this point, we have a validated input field for dotted IP addresses that are stored in the database as integers (which allows us to sort them) and displays the dotted IP to the user when editing the value. The AllItems and Display page don't retrieve the value for a list item using the GetFieldValueForEdit method. Instead, I use some javascript in a RenderPattern in the fldtypes*.xml file. Seems like time for another SharePoint tip brought to you by &lt;a href="http://www.amazon.com/Microsoft-Windows-SharePoint-Services-Developer/dp/0735623201"&gt;Inside Windows SharePoint Services 3.0&lt;/a&gt; written by Ted Pattison and Daniel Larson, if you don't own it, you're probably not a SharePoint developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SharePoint Tip #911&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you find yourself enjoying CAML, seek professional help immediately.&lt;/p&gt;
&lt;p&gt;If you've developed any custom field types, then you are already familiar with the Field Type Definition file located in TEMPLATE/XML/fldtypes*.xml. Here is the display render pattern I used to convert the numeric IP into a dotted decimal version for viewing in lists. It's broken down into separate lines here, but needs to be all on one line in the actual file.&lt;/p&gt;
&lt;pre&gt;
&amp;lt;HTML&amp;gt;&amp;lt;![CDATA[&amp;lt;SCRIPT&amp;gt;var ipnumber=]]&amp;gt;&amp;lt;/HTML&amp;gt;
&amp;lt;Column/&amp;gt;
&amp;lt;HTML&amp;gt;&amp;lt;![CDATA[;document.write(
Math.floor(ipnumber/16777216)%256 + '.'
+ Math.floor(ipnumber/65536)%256 + '.'
+ Math.floor(ipnumber/256)%256 + '.'
+ Math.floor(ipnumber)%256);&amp;lt;/SCRIPT&amp;gt;]]&amp;gt;
&amp;lt;/HTML&amp;gt;
&lt;/pre&gt;
&lt;p&gt;And now we acheived the same thing as the calculated column above but without having to have 2 fields.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 508px; height: 468px" src="http://i.nfectio.us/images/ipcft/IPAddressField.png" border="0" alt="IP Address Field" title="IP Address Field" width="508" height="468" /&gt;&lt;/p&gt;
&lt;p&gt;There is one small gotcha that I haven't worked around yet. Sorting and everything works, but when you open the sort menu, it displays the IPs in their numeric form. Unfortunately, SharePoint doesn't seem to chose only one method to grab the values from the database. This is a small enough gotcha that I won't worry about it for now. If you can figure it out, please let me know so I can update the code.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 227px; height: 193px" src="http://i.nfectio.us/images/ipcft/SmallBug.png" border="0" alt="" width="227" height="193" /&gt;&lt;/p&gt;
&lt;p&gt;You can download the source for this Custom Field &lt;a href="http://i.nfectio.us/images/ipcft/IPAddressField.zip"&gt;here&lt;/a&gt; and if you just want to use it, you can download a solution &lt;a href="http://i.nfectio.us/images/ipcft/UMESD.CFTS.IPAddressField.wsp"&gt;file&lt;/a&gt; to install it.&lt;/p&gt;</description>
      <pubDate>Sun, 21 Oct 2007 21:23:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:ea7fe157-f994-446e-91b8-76cefadb5352</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2007/10/21/ip-address-custom-field-type-in-sharepoint</link>
      <category>SharePoint</category>
      <category>SharePoint</category>
      <category>Custom Field Type</category>
    </item>
    <item>
      <title>From Rails to SharePoint</title>
      <description>&lt;p&gt;
If anyone still has my blog in their RSS readers, they haven't seen anything for quite a long time now. So what gives? My initial foray into Ruby and Rails was a great ride but now I've moved on to something else which is occupying my time much more than RoR ever did. Microsoft Office SharePoint Server 2007. Yea, yea, I know what most of you are thinking but SharePoint is truly an awesome product, especially for the environment that I work in. I've spent the last 10 years writing apps similar to SharePoint to support our business processes. Each one got a little closer to what SharePoint is, but all fell way short (obviously, I'm only one guy).
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
I'm not dropping Ruby/Rails though, I developed a few in house apps with it that I will continue to support, and I am even looking at a Ruby library to support SharePoints extensive Web Service architecture. The main point of this post (since I doubt anyone really cares what I do with my time) is that this blog will provide a mixture of RoR and SharePoint content.
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;h3&gt;So lets get started with my first SharePoint Tip:&lt;/h3&gt;
&lt;br /&gt;
&lt;strong&gt;Development is a bitch! (sometimes)&lt;/strong&gt;
&lt;br /&gt;&lt;br /&gt;
Thats it for now, stay tuned as I prepare a write-up on a CFT that wraps text around a text field in New/Edit pages.
&lt;br /&gt;&lt;br /&gt;
&lt;img src="/images/wrappedtextfield.png" border="0" /&gt;
</description>
      <pubDate>Sat, 20 Oct 2007 19:14:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:fb717cfc-f675-45db-b028-f4745ec8a23a</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2007/10/20/from-rails-to-sharepoint</link>
      <category>SharePoint</category>
      <category>SharePoint</category>
    </item>
    <item>
      <title>RAILS_ROOT and Dir.chdir</title>
      <description>&lt;p&gt;It's been awhile since I've blogged on Rails, got diverted to a Novell to Windows conversion over Christmas break. A local community college requested emergency assistance with completing their conversion and asked us to do it in 2 weeks. My team and I got it done but it was a hell of a lot of work. Now that I'm back to programming, I ran into a little annoyance last night that took awhile to troubleshoot down. I'm working on a little app to provide a web interface to my book collection. My book repository is located elsewhere on my harddrive so I used Dir.chdir to move to the root of the repository.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
To make a long story short, DO NOT use Dir.chdir in your Rails Applications. RAILS_ROOT, which is used to locate dependencies is a relative path. If you change the working directory of your Rails application, Rails may/will have problems loading dependencies. In my case, I used Dir.chdir to move to then iterate the publisher directories and add books found onto the publisher.books has_many association. This promptly failed with Publisher::Book uninitialized constant errors. The dependency loading mechanism uses RAILS_ROOT to find items in your load_paths and being a relative path, changes if you change the current working directory.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;I believe one solution, which I didn't test, would be to set RAILS_ROOT to an absolute path in your environment.rb. The solution I chose was to not chdir but use a full path in each Directory or File operation i used. And now you know.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 13:29:00 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:bf184585-13b0-4f89-8024-c05fc49cf1e8</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2007/02/10/rails_root-and-dir-chdir</link>
      <category>Ruby on Rails</category>
      <category>ruby</category>
      <category>rails</category>
      <category>chdir</category>
    </item>
    <item>
      <title>ActiveRecord Calculations</title>
      <description>I've been wanting to modify the ActiveRecord Calculations module to support multiple aggregate calculations in one query for awhile now. My current project with lots of graphing finally gave me the motivation to do it. Refer to the patch for details and feel free to comment on it.

&lt;pre&gt;
# Calculate different aggregate functions at once
min, max = Person.calculate(:min, :age, :max, :age)

# Specify the column aliases so you can order by it
views, replies = Posts.sum({:views =&gt; 'number_of_views'}, :replies, :order =&gt; "number_of_views desc") 
&lt;/pre&gt;

&lt;a href="http://dev.rubyonrails.org/ticket/6552"&gt;Multiple Calculations Patch&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;</description>
      <pubDate>Sun, 05 Nov 2006 12:38:00 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:f9e40c1f-0b94-48a4-81b7-d04a0f9475cd</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/11/05/activerecord-calculations</link>
      <category>Ruby on Rails</category>
      <category>rails</category>
      <category>patch</category>
      <category>calculations</category>
    </item>
    <item>
      <title>Deprecations in Rails 1.2</title>
      <description>Here is a quick list of deprecations in Rails 1.2. Update your code now to make upgrading easier later. Some of these have actually been around since before the current release of 1.1.6 but I thought I'd mention them anyways.

&lt;pre&gt;
Deprecations in Rails 1.2

* Passing method reference symbols to ActionController::Base#url_for. Use named routes instead.
* Calling ActionController::Base#render with string arguments. Ex: render('weblog/show')
* Using model, service, observer, depend_on and dependencies_on in your controller. Rails handles most dependency issues automatically now.
* Calling keep_flash instead of flash.keep
* Pagination will be extracted out into a plugin in Rails 2.0
* Auto complete will be extracted out into a plugin in Rails 2.0
* In place editing will be extracted out into a plugin in Rails 2.0
* start_form_tag and end_form_tag have been deprecated. form_tag still works the same, but there is no wrapper method for the &lt;/form&gt;. The preferred method is to use the new block form of form_tag. Ex: form_tag :action =&gt; :create do ... end
* The number helper method human_size is deprecated in favor of number_to_human_size
* link_to_image and link_image_to helpers are deprecated in favor of wrapping an image_tag in a link_to. Ex: link_to(image_tag(...), :action =&gt; :new)
* Using @cookies, @flash, @headers, @params, @request, @response or @session has been deprecated. Use the method form without the @. Ex: response.content_type
* Calling content_for :layout || :symbol. Use yield || yield :symbol instead
* Calling image_tag without an extension is deprecated. Auto appending .png will be removed. Don't be lazy.
* Passing :post as a link_to modifier to force a POST request via a link tag. Use :method =&gt; :post instead.
* push_with_attributes and concat_with_attributes on a HABTM association are deprecated. Use has_many :through with a join model instead.
* find_all and find_first on a has_many association. Use find(:all) and find(:first)
* :dependent =&gt; true on a has_many association. Use :dependent =&gt; :destroy
* :exclusively_dependent =&gt; true on a has_many association. Use :dependent =&gt; :delete_all
* When using :class_name on a belongs_to association, the foreign_key name will be inferred from the association rather than the :class_name. Therefor, use :foreign_key if using :class_name and they differ.
* A welcome deprecation, internally, the quote method of ActiveRecord has been renamed. After Rails 2.0 you will be able to use a column named quote again.
* Calling count on a model with string arguments for conditions and joins. Use an options hash instead. Ex: Model.count(:conditions =&gt; '...')
* Validations helper method add_on_boundary_breaking in favor of validates_length_of
* Hash extension create_from_xml has been renamed to from_xml
* All the old rake tasks have been moved to namespaced task, old ones are deprected. Ex: rake db:migrate instead of rake migrate
&lt;/pre&gt; </description>
      <pubDate>Thu, 02 Nov 2006 11:40:00 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:edae725b-2a3d-431a-b4ab-77b9c82f24d6</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/11/02/deprecations-in-rails-1-2</link>
      <category>Ruby on Rails</category>
      <category>deprecations</category>
      <category>rails</category>
    </item>
    <item>
      <title>Rails Documentation</title>
      <description>&lt;p&gt;Improving the Rails documentation has been an ongoing discussion for awhile now. Over $15,000 has been raised by the community to improve them but nothing has come from that yet. I'm going to outline some of my thoughts about it here. These are just random thoughts and do not provide complete solutions. &lt;em&gt;NOTE: This is not a rant against court3nay or the Rails Documentation Project. It's only my opinion on how it could have been handled differently. And for full disclosure, I did not donate any money to the project, I'd rather donate my time.&lt;/p&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Not to take anything away from court3nay but the timing of the Rails Documentation Project was off for several reasons. First, you should have a game plan before you ask for the money. It's been many months now since everyone donated and nothing has been done with the money. This leads to other problems, mainly it breaks the trust of the community. The next time something worthy comes along, likely contributors will be a little more cautious before they hit that donate button.&lt;/li&gt;
&lt;li&gt;With everyone waiting to see what comes from the Rails Documentation Project, the number of patches for documentation to core has dropped.&lt;/li&gt;
&lt;li&gt;Has anyone answered exactly what is wrong with the existing documentation? I'll outline a few things I see:
&lt;ul&gt;
&lt;li&gt;Easily gets out of date, need to place documentation on the same level as tests when accepting or rejecting patches.&lt;/li&gt;
&lt;li&gt;The API docs on rubyonrails.org need a version selector. People using older versions of Rails (for whatever reason), should be able to access the appropriate docs for their version. This problem will be even more evident once Rails starts shipping with OS X and other Linux distros. The public website should support this, not a message explaining how to generate local documentation for your Rails version.&lt;/li&gt;
&lt;li&gt;No style guide. You have to refer to existing documentation to determine the style to use.  Could be improved with a style guide.&lt;/li&gt;
&lt;li&gt;Are they docs or an API reference? Currently, its a combination of both and both are lacking. I think the examples need to be expanded (and possibly even follow a common theme, more on this later) and the API's more complete. When rewriting the docs for some of the ActionView Helpers, I found a quite a few options that weren't documented.&lt;/li&gt;
&lt;li&gt;Core should have a dedicated person(s) to handle documentation issues. Making sure patches are properly documented (or willing to do it themselves) and reviewing/committing documentation patches. This person should also create the style guide or written standard for documentation.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;I see two different audiences for documentation. The new users who really need a tutorial like approach and the more experienced who really need an accurate API reference. I'm not sure the inline documentation in Rails can serve both audiences, but it can come closer than it does currently. I think it would be great if Rails shipped with a default example application that all the documentation examples pulled from and referenced back to. This maintains a common theme throughout and acts as an excellent teaching tool.&lt;/li&gt;
&lt;li&gt;So what should be done with the $15,000? Pay an author (a good one preferably) to write an open source book on Rails. This book should be completed by the author and then maintained by the community to stay current with Rails development. This is the best way I can see to spend the money.&lt;/li&gt;
&lt;/ul&gt;

&lt;strong&gt;Update:&lt;/strong&gt; Better explanation of the "open source book" idea.
&lt;pre&gt;
By open source book, I mean a separately maintained API reference 
maintained in book form, organized into logical chapters and topics
with full API coverage and plenty of examples. Take the API docs 
for AR associations. ActiveRecord and its associations are documented
across many files making it difficult to provide full and complete
examples of its many use cases inline. Having a dedicated chapter to
cover each association with full example coverage and feedback 
capability would be better than the inline docs. The inline docs 
should provide full API coverage, but leave the examples out. This
other "officially" maintained documentation would contain the 
example coverage and many use cases as well as user feedback.

Yes this is extra work, but I think you could find plenty of 
volunteers, including myself, to make up an official rails-doc 
team with full commit rights to the documentation in rails trunk.
&lt;/pre&gt;</description>
      <pubDate>Wed, 25 Oct 2006 10:08:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:089605df-c332-4670-983f-85d819a5419b</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/10/25/rails-documentation</link>
      <category>Ruby on Rails</category>
      <category>rails</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Planes, Cigarettes and Snowmobiles</title>
      <description>It's an early Christmas for me this year. I'm heading off to RubyConf tomorrow and I picked up my new snowmobile last friday. Following through on my committment to stop smoking once my snowmobile arrived, if you need to find me at RubyConf, just look around the room for the guy with the cold sweats and convulsions, that will be fearoffish, he just quit smoking too, I'll be beside him passed out on the floor. 
&lt;br /&gt;&lt;br /&gt;
Cheers, hope to see you there!!</description>
      <pubDate>Wed, 18 Oct 2006 17:59:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:8bfa3cae-fca2-4006-a748-4d9645f52ea2</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/10/18/planes-cigarettes-and-snowmobiles</link>
      <category>Ruby on Rails</category>
      <category>rubyconf</category>
      <category>snowmobile</category>
    </item>
    <item>
      <title>Testing Gotchas in Rails</title>
      <description>I just spent the past several hours tracking down an issue with my tests. I use a combination of autotest from the &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;ZenTest Suite&lt;/a&gt; and &lt;a href="http://blog.codahale.com/2006/05/26/rails-plugin-rails_rcov/"&gt;rails_rcov&lt;/a&gt; for my Rails testing. While testing using autotest, all my tests were passing just fine. I then went to check test-code coverage and all of a sudden I have a failing test.&lt;br /&gt;&lt;br /&gt;

* rake test:functionals:rcov - failed.&lt;br /&gt;
* rake test:functionals - failed&lt;br /&gt;
* ruby test/functional/clients_controller_test.rb - passed&lt;br /&gt;
* autotest - passed

&lt;br /&gt;&lt;br /&gt;
So what gives? Basically, when running tests using the rake tasks, the test databases are recreated each time you run the task via rake db:test:clone. The real problem was that I was failed to load one of my fixtures in my functional test, but since the data existed from a previous test which used those fixtures, the tests passed. But when running the test from the rake task which resets all the tables, no fixture data existed in the database causing the difference in behavior between the two tools.
&lt;br /&gt;&lt;br /&gt;
As always, if your tests are hokie, CHECK YOUR FIXTURES FIRST! Enjoy this tip.&lt;br /&gt;&lt;br /&gt;</description>
      <pubDate>Mon, 09 Oct 2006 21:07:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:dbaec1b3-4d16-4dbf-9813-ebc13cebcdfa</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/10/09/testing-gotchas-in-rails</link>
      <category>Ruby on Rails</category>
      <category>rails</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
