Showing posts with label SharePoint 2010. Show all posts
Showing posts with label SharePoint 2010. Show all posts

Monday, April 16, 2012

F# and SharePoint 2010 Object Hierarchy/Properties

I had the opportunity to take a SharePoint 2010 class recently. In the class, the labs were mostly a cut and paste affair due to time limitations. Those lab exercises only helps me to become familiar with working in Visual Studio and seeing some of the SharePoint API, but does not really help me engage actively in thinking about what I was actually doing. After the class, I decided to write a F# equivalent of the lab exercises to help me get a deeper understanding and to learn. The class did impress on me that the tooling for SharePoint 2010 development is so much more superior in C# such that I've decided to build only the logic code in F# and retain the C# SharePoint project. This blog post describes my effort in getting that first class exercise working with F# code.

I ended up creating an empty SharePoint 2010 C# project that references a F# library project. I added two application pages to the SharePoint 2010 project, the first being FarmHierarchy.aspx. In FarmHierarchy.aspx, I added the following markup between the opening and closing tags of the <asp:Content> element that has an ID of Main:

<h2>My Farm</h2>
<asp:TreeView ID="farmHierarchyViewer" runat="server"
    ShowLines="true" EnableViewState="true"></asp:TreeView>

The second application page I created was PropertyChanger.aspx. I added the following markup between the opening and closing tags of the <asp:Content> elsement that has an ID of Main:

    <h2>Properties:</h2>
    <asp:Label ID="objectName" runat="server" Text=""></asp:Label><br/><br/>
    <asp:Panel ID="webProperties" runat="server" Visible="false" BorderColor="Orange" BorderStyle="Dashed" BorderWidth="1">
        <asp:Label ID="WebLabel" runat="server" Text="Web Title"></asp:Label>
        <br/>
        <asp:TextBox ID="webTitle" runat="server" EnableViewState="true"></asp:TextBox>
        &nbsp;
        <asp:Button ID="webTitleUpdate" runat="server" Text="Update"/>
        &nbsp;
        <asp:Button ID="webCancel" runat="server" Text="Cancel" />
    </asp:Panel>
    <asp:Panel ID="listProperties" runat="server" Visible="false" BorderColor="Orange" BorderStyle="Dashed" BorderWidth="1">
        <asp:Label ID="ListLabel" runat="server" Text="List Properties"></asp:Label>
        <br/>
        <asp:CheckBox ID="listVersioning" runat="server" EnableViewState="true" Text="Enable Versioning" />
        <br/>
        <asp:CheckBox ID="listContentTypes" runat="server" EnableViewState="true" Text="Enable Content Types" />
        &nbsp;
        <asp:Button ID="listPropertiesUpdate" runat="server" Text="Update" />
        &nbsp;
        <asp:Button ID="listCancel" runat="server" Text="Cancel"/>
    </asp:Panel>

The F# code would iterate through the services, Web applications, site collections, and lists in the SharePoint farm, with the details added to nodes in the TreeView control:


module FsLab01

open System
open System.Web.UI.WebControls
open Microsoft.SharePoint
open Microsoft.SharePoint.Administration
open System.Web.UI

// Convenience methods
let nullfunc _ = ()

// Copied from Clojure
let cond clauses =
    let (_,func) = clauses |> Seq.find (fun (pred,func) -> pred)
    func()


let navListUrl url (id:Guid) =
    sprintf "%s/_layouts/lab01/PropertyChanger.aspx?type=list&objectID=%s" url <| id.ToString()


let navWebUrl url (id:Guid) =
    sprintf "%s/_layouts/lab01/PropertyChanger.aspx?type=web&objectID=%s" url <| id.ToString()

// Recursively add SPWeb & SPList objects
let rec addWeb (web:SPWeb) (parentNode:TreeNode) =

    let node = new TreeNode(web.Title,null,null,navWebUrl web.Url web.ID, "_self")
    node |> parentNode.ChildNodes.Add

    [0..(web.Lists.Count-1)]
    |> Seq.map (fun i -> web.Lists.[i])
    |> Seq.iter (fun item -> 
                      new TreeNode(item.Title,null,null,navListUrl web.Url item.ID,"_self")
                      |> node.ChildNodes.Add)
        
    [0..(web.Webs.Count-1)]
    |> Seq.map (fun i -> web.Webs.[i])
    |> Seq.iter (fun item -> try addWeb item node finally item.Dispose())


// Main function to be called by FarmHierarchy.aspx code to build TreeView control
let loadViewer (viewer:TreeView) (farm:SPFarm) =

    let processSite (webappnode:TreeNode) (site:SPSite) =
        site.CatchAccessDeniedException <- false
        try
            let node = new TreeNode(Text=site.Url)
            node |> webappnode.ChildNodes.Add

            addWeb site.RootWeb node

        finally
            site.CatchAccessDeniedException <- false
            

    let processWebApp (svcNode:TreeNode) (webapp:SPWebApplication) = 
        let node = new TreeNode(Text=webapp.DisplayName )
        node |> svcNode.ChildNodes.Add

        cond [(not webapp.IsAdministrationWebApplication,
               fun _ -> webapp.Sites |> Seq.iter (processSite node));
              (true,nullfunc)]
            
                

    let processService (svc:SPService) =
        let label = sprintf "FarmService (Type=%s; Status=%s)" (svc.TypeName) (svc.Status.ToString())
        let node = new TreeNode(Text=label)
        node |> viewer.Nodes.Add
        match svc with
        | :? SPWebService as websvc -> websvc.WebApplications |> Seq.iter (processWebApp node)
        | _ -> ()

    
    viewer.Nodes.Clear()
    farm.Services |> Seq.iter processService
    viewer.ExpandAll()

As I was creating loadViewer method, I was bothered by the if-else-then clauses in the code. I had a previous blog that talked about this issue when it struck me that what I really wanted was something similar to the cond macro in Clojure. Hence the convenience function called cond in the above F# code.

With the F# code written and packaged as a library, I can now use the above F# function in FarmHierarchy.aspx as follows:

        protected void Page_Load(object sender, EventArgs e)
        {
            SPFarm thisFarm = SPFarm.Local;
            FsLab01.loadViewer(farmHierarchyViewer, thisFarm);
        }

The second part of this lab was to create code to manipulate properties of SharePoint SPWeb or SPList objects. The F# code that manipulates the SharePoint object properties and the UI are as follow:



let changeProperty (page:Page) (objectName:Label) (webtitle:TextBox) 
                   (listpanel:Panel) (webpanel:Panel) 
                   (listVersioning:CheckBox) (listContentTypes:CheckBox) 
                   (webUpdateBtn:Button) (listUpdateBtn:Button)
                   (webCancelBtn:Button) (listCancelBtn:Button)   =    


    let homeurl  baseurl = sprintf "%s/_layouts/lab01/FarmHierarchy.aspx" baseurl

    let wrapupdates (web:SPWeb) action =
        web.AllowUnsafeUpdates <- true
        action()
        web.AllowUnsafeUpdates <- false
        

    let hidepanels () =
        listpanel.Visible <- false
        webpanel.Visible <- false

    let cancel (web:SPWeb) =
        page.Response.Redirect(homeurl web.Url)
    
        
    let checkNull item = 
        cond [(item=null,  fun _ -> objectName.Text <- "Malformed URL"
                                    hidepanels()
                                    "");
              (item<>null, fun _ -> item.ToString())]


    try
        let objectType = checkNull page.Request.["type"]
        let objectId = checkNull page.Request.["objectID"]

        match objectType with
        | "web" -> 
            listpanel.Visible <- false
            webpanel.Visible <- true
            use web = SPContext.Current.Site.OpenWeb(new Guid(objectId))

            // Hook up the events
            let myupdates _ =
                web.Title <- webtitle.Text
                web.Update()

            webUpdateBtn.Click.Add(
                fun _ ->
                    try 
                        wrapupdates web myupdates

                        page.Response.Redirect(homeurl web.Url)
                    with ex ->
                        objectName.Text <- ex.Message
                        hidepanels()
                )

            webCancelBtn.Click.Add(fun _ -> cancel web)

            objectName.Text <- sprintf "Web: %s" web.Title

            cond [(not page.IsPostBack,
                   fun () -> webtitle.Text <- web.Title);
                   (true,nullfunc)]

        | "list" -> 
            listpanel.Visible <- true
            webpanel.Visible <- false
            let web = SPContext.Current.Web
            let splist = web.Lists.[new Guid(objectId)]


            let myupdates _ =
                splist.EnableVersioning <- listVersioning.Checked
                splist.ContentTypesEnabled <- listContentTypes.Checked
                splist.Update()

            listUpdateBtn.Click.Add(
                fun _ ->
                    try 
                        wrapupdates web myupdates
                        page.Response.Redirect(homeurl web.Url)
                    with ex ->
                        objectName.Text <- ex.Message
                        hidepanels())

            listCancelBtn.Click.Add(fun _ -> cancel web)

            cond [(not page.IsPostBack,
                   fun _ -> listVersioning.Checked   <- splist.EnableVersioning
                            listContentTypes.Checked <- splist.ContentTypesEnabled)]
        | _ -> ()


    with  ex-> objectName.Text <- ex.Message

I needed to sign my F# code in order it to work in SharePoint. Since the C# SharePoint 2010 already created the key, all I had to do is to add --keyfile:<Location to keyfile>\key.snk to the F# build setting's "Other Flags" properties. In addition, before I can build and deploy, I needed to add the F# library dll to the C# SharePoint project's package. I can do so by clicking on Package in the SharePoint project and then click on the Advanced button.

I can now call this F# function from PropertyChanger.aspx code as follows:

            FsLab01.changeProperty(this.Page, objectName, webTitle,
                                   listProperties, webProperties, 
                                   listVersioning, listContentTypes,
                                   webTitleUpdate, listPropertiesUpdate,
                                   webCancel,listCancel);

Here's how what FarmHierarchy.aspx would look like :

Here's how what PropertyChanger.aspx would look like if I wanted to modify a SPWeb object:

Here's how what PropertyChanger.aspx would look like if I wanted to modify a SPList object:

Monday, March 12, 2012

F# and SharePoint 2010

I bought a bunch of SharePoint 2010 books and haven't had the time to go through those books. Recently, I finally had some time to take look at those books and try it out in F#. Trying out examples with SharePoint 2007 was fairly painless as I get to do everything in a 32-bit environment. Trying to get Visual Studio 2010 F# to work with SharePoint 2010 was more challenging. While I was able to get the compiled F# code to work, I couldn't overcome the hurdles to get SharePoint 2010 to work with F# Interactive, which is really my prefer way to investigate SharePoint server object models. I can tell from this Stack Overflow entry that I was not alone in having trouble to get F# interactive to work with SharePoint API. The combination of .NET 3.5 framework and 64-bit defeated my effort at getting it to work. I have tried Igor Dvorkin's steps to run F# interactive in 64-bit and then taking the command line arguments for the F# compiler and apply to F# interactive where applicable. So I would start up my F# interactive with the following command line parameters:

"C:\Program Files (x86)\Microsoft F#\v4.0\fsi.exe" ^
  --debug:full ^
  --noframework ^
  --define:DEBUG ^
  --define:TRACE ^
  --optimize- ^
  --tailcalls- ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0\FSharp.Core.dll" ^
  -r:"C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.dll" ^
  -r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" ^
  -r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\System.ServiceModel.dll" 

When I try to run with the above arguments, I get the following error:

error FS0084: Assembly reference 
'C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0\FSharp.Compiler.Interactive.Settings.dll' 
was not found or is invalid 

So something in FSharp.Compiler.Interactive.Settings.dll is not working with .NET Framework 3.5. I wish there was an option in F# interactive that allows me to target the runtime .NET framework to use (e.g. --target:3.5)

The good news is that the F# compiler works fine as long as you make sure that you are targeting .NET 3.5 runtime and x64 CPU. Here's the F# version of a sample code that I pulled from the book Inside Microsoft SharePoint 2010 by Ted Pattison, Andrew Connell, Scott Hillier and David Mann

open System
open Microsoft.SharePoint

let siteUrl = "http://localhost/"
let sites = new SPSite(siteUrl)

let site = sites.RootWeb
http://www.blogger.com/blogger.g?blogID=18281936#editor/target=post;postID=3313071912798963533
seq {for i in 0..(site.Lists.Count-1) do
       if site.Lists.[i].Hidden <> true then
         yield (site.Lists.[i]) }
|> Seq.iter (fun item -> printf "%s\n" item.Title)

For those who have read my past posts on F# and SharePoint and my past blog post on Revisiting the SharePoint collection adapter for F#, I am sorry to say that I have been led astray by C# example codes and my brain temporary malfunctioned as it produced the SharePoint adapter nonsense in that previous blog post. There was no need to write any SharePoint utility library in C# to get F# to work with SPListCollection as sequences. In the above example, I used sequence expressions to generate SPList sequences from SPListCollection. Another way is to simply create a sequence by use map function as shown in the following example:

// Create a sequence by mapping over all items in the collection
let spcollection = site.Lists
[0..spcollection.Count-1] 
|> Seq.map (fun i -> spcollection.[i])
|> Seq.iter (fun item -> printf "%s\n" item.Title)

There was no need to leave the confines of F# development and it is also type safe compared to my previous clumsy attempt at converting SPListCollection into a sequence. If you really do want to use an adapter, it could also be implemented in F# as follows:

// SPCollectionAdapter written in F#
let fromSPCollection<'a,'b when 'b :> SPBaseCollection> (collection:'b) =
    let enumerator = collection.GetEnumerator()
    let rec enumerate (e:Collections.IEnumerator) acc =
        let flag = e.MoveNext()
        if flag = false then
            acc
        else
            enumerate e (e.Current :?> 'a :: acc)
    enumerate enumerator [] 

// Example usages
fromSPCollection<SPList,SPListCollection> site.Lists
|> Seq.iter (fun item  -> printf "%s\n" item.Title)
    

// webapp is an instance of SPWebApplication object
fromSPCollection<SPContentDatabase,SPContentDatabaseCollection>  webapp.ContentDatabases
|> Seq.iter (fun db -> 
              printf "Content Database : %s\n" db.Name
              printf "Connection String : %s\n" db.DatabaseConnectionString)