Author: Sergey Tihon 🦔🦀🦋
F# Weekly #43, 2012
A brief summary of what happened in the blogs last week:
- Released new F# Online Tutorial
- F# 3.0 Rich Functional Programming Talk
- Mono 3.0 is out!
- .NET Rocks – Don Syme and Keith Battocchi Bring F# Everywhere
- Updated F# Language Specification for F# 3.0 Now Available
- F# Compiler (open source edition)
- The future of .NET lies in Mono. The future of F# lies in MonoDevelop
- Try F# Sabbatical
- Crisis in Open Source Land
- F# Continuation Style Programming
- Three Free Ways to Develop in F# 3.0
- Fix F# WinRT Application Verification Problem
- Using F# Records with ASP.NET Web API
P.S. Thanks to Don Syme and Richard Minerich for idea.
OData Type Provider with SharePoint 2010
‘Type Providers’ is an extremely cool F# feature that was introduced with F 3.0 and shipped with VS 2012 and .NET 4.5. You can find Type providers’ explanation here and details about OData type provider here.
It maybe not a news, but OData Type Provider works pretty well with SharePoint 2010.
SharePoint 2010 has a OData Service. You can find mode details about this service at the MSDN article Query SharePoint Foundation with ADO.NET Data Services
One special thing that distinguishes SharePoint service from the others is that you should be authenticated. Just set your credentials into created data content before using it. You can find full code sample below.
#r "FSharp.Data.TypeProviders.dll"
#r "System.Data.Services.Client.dll"
open Microsoft.FSharp.Data.TypeProviders
open System.Net
type sharepoint = ODataService<"http://server_name/_vti_bin/listdata.svc">
let web = sharepoint.GetDataContext() in
web.Credentials <-
(NetworkCredential("user_name", "password", "domain") :> ICredentials)
(web.Documents)
|> Seq.iter (fun item -> printfn "%A : %s" item.Modified item.Name)
64bit fsi (FSharp Interactive) already available !!!
The good news for all F#/SharePoint lovers! As I learned today, 64 bit version of fsi already available as a part of F# 3.0. It is FsiAnyCPU.exe (at C:\Program Files (x86)\Microsoft SDKs\F#\3.0\Framework\v4.0).
These mean that now we should be able to connect to the SharePoint directly from fsi. It’s really cool !!!
As I see, into vs2012 we can choose version of fsi from Tools->Options.
P.S. Great thanks to the Mauricio Scheffer for great news.
How to delete broken EventReceiverDefinitions
When you use SharePoint 2010 event receivers for sites, webs or list you may get a broken event receiver definitions. It could be due to incorrect event receivers managment, you used packages that left broken event receiver definitions or something like that.
It is too hard to manually remove all broken definitions from whole site collection. I do not know useful tool for this purpose.
The following script do it for you. It is iterates through all webs and lists into site collection and remove all event receiver definitions that point to not exist assemblies.
open Microsoft.SharePoint
module EventReceiversCleaner =
let private isAssemblyExist (assemblyName:string) =
try
match System.Reflection.Assembly.Load(assemblyName) with
| null -> false
| assembly -> assemblyName = assembly.FullName
with
| e -> false
let private removeCandidates = ref List.Empty
let CollectBroken (collection:SPEventReceiverDefinitionCollection) =
for er in collection do
if not (isAssemblyExist er.Assembly) then
removeCandidates := er :: !removeCandidates
let RemoveAll() =
!removeCandidates |> List.iter
(fun (er:SPEventReceiverDefinition) ->
let name = sprintf "Assembly:'%s'" er.Assembly
try
er.Delete()
printfn "Deleted : %s" name
with
| e -> printf "Failed to delete: %s" e.Message)
try
let url = "http://localhost/"
printfn "Connecting to '%s'..." url
use site = new SPSite(url)
site.EventReceivers |> EventReceiversCleaner.CollectBroken
let rec collectFromLists (web:SPWeb) =
printfn "Processing web '%s'..." web.ServerRelativeUrl
web.EventReceivers |> EventReceiversCleaner.CollectBroken
web.Webs |> Seq.iter collectFromLists
for list in web.Lists do
printfn "Processing list '%s'..." list.Title
list.EventReceivers |> EventReceiversCleaner.CollectBroken
use web = site.OpenWeb()
collectFromLists web
EventReceiversCleaner.RemoveAll()
printfn "Finished."
with
| e -> printfn "Exception : %s" e.Message
System.Console.ReadLine() |> ignore
P.S. You should compile it using .NET 3.5 and 64 bit project.
Configuring Sublime Text 2 To Work With FSharp
I like working with the Sublime Text 2 editor and I surely like to work with FSharp so I’ve been trying to find ways to make life easier for myself. A few tips to pass along:
- Get the F# Textmate Bundle. Once you get it, unzip it into the ~\Application Data\Sublime Text 2\Packages directory. In my case I created a F# directory under the Packages directory.
- Add the following settings via the File Settings-User menu
- To make it easier to compile and build your app, create a couple of environment variables: MSBuildBin and FSBin. In my case I set the MSBuildBin to the directory where the MSBuild.exe is located and I set FSBin to the directory where Fsc.exe is located.
Under the ~\Application Data\Sublime Text 2\Packages\User directory, create a file called msbuild.sublime-build and put the following in it:
Once you do this, on the Tools->Build System…
View original post 155 more words
Functional programming is a ghetto
LinkedIn OAuth in F#
First of all great thanks to Luke Hoban for his ‘Twitter OAuth in F#‘. His OAuth implementation works fine for LinkedIn as well.
All you need to do is to switch the requestTokenURI/accessTokenURI/authorizeURI addresses from Twitter on LinkedIn.
Additional materials about LinkedIn public API you found at the LinkedIn developers site: https://developer.linkedin.com/
LinkedIn OAuth implementation:
open System
open System.IO
open System.Net
open System.Security.Cryptography
open System.Text
// LinkedIn OAuth Constants
let consumerKey : string = failwith "Must provide the consumerKey for an app registered at https://www.linkedin.com/secure/developer?newapp="
let consumerSecret : string = failwith "Must provide the consumerSecret for an app registered at https://www.linkedin.com/secure/developer?newapp="
let requestTokenURI = "https://api.linkedin.com/uas/oauth/requestToken"
let accessTokenURI = "https://api.linkedin.com/uas/oauth/accessToken"
let authorizeURI = "https://api.linkedin.com/uas/oauth/authorize"
// Utilities
let unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
let urlEncode str =
String.init (String.length str) (fun i ->
let symbol = str.[i]
if unreservedChars.IndexOf(symbol) = -1 then
"%" + String.Format("{0:X2}", int symbol)
else
string symbol)
// Core Algorithms
let hmacsha1 signingKey str =
let converter = new HMACSHA1(Encoding.ASCII.GetBytes(signingKey : string))
let inBytes = Encoding.ASCII.GetBytes(str : string)
let outBytes = converter.ComputeHash(inBytes)
Convert.ToBase64String(outBytes)
let compositeSigningKey consumerSecret tokenSecret =
urlEncode(consumerSecret) + "&" + urlEncode(tokenSecret)
let baseString httpMethod baseUri queryParameters =
httpMethod + "&" +
urlEncode(baseUri) + "&" +
(queryParameters
|> Seq.sortBy (fun (k,v) -> k)
|> Seq.map (fun (k,v) -> urlEncode(k)+"%3D"+urlEncode(v))
|> String.concat "%26")
let createAuthorizeHeader queryParameters =
let headerValue =
"OAuth " +
(queryParameters
|> Seq.map (fun (k,v) -> urlEncode(k)+"\x3D\""+urlEncode(v)+"\"")
|> String.concat ",")
headerValue
let currentUnixTime() = floor (DateTime.UtcNow - DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds
/// Request a token from LinkedIn and return:
/// oauth_token, oauth_token_secret, oauth_callback_confirmed
let requestToken() =
let signingKey = compositeSigningKey consumerSecret ""
let queryParameters =
["oauth_callback", "oob";
"oauth_consumer_key", consumerKey;
"oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
"oauth_signature_method", "HMAC-SHA1";
"oauth_timestamp", currentUnixTime().ToString();
"oauth_version", "1.0"]
let signingString = baseString "POST" requestTokenURI queryParameters
let oauth_signature = hmacsha1 signingKey signingString
let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters
let req = WebRequest.Create(requestTokenURI, Method="POST")
let headerValue = createAuthorizeHeader realQueryParameters
req.Headers.Add(HttpRequestHeader.Authorization, headerValue)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
let txt = (new StreamReader(stream)).ReadToEnd()
let parts = txt.Split('&')
(parts.[0].Split('=').[1],
parts.[1].Split('=').[1],
parts.[2].Split('=').[1] = "true")
/// Get an access token from LinkedIn and returns:
/// oauth_token, oauth_token_secret
let accessToken token tokenSecret verifier =
let signingKey = compositeSigningKey consumerSecret tokenSecret
let queryParameters =
["oauth_consumer_key", consumerKey;
"oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
"oauth_signature_method", "HMAC-SHA1";
"oauth_token", token;
"oauth_timestamp", currentUnixTime().ToString();
"oauth_verifier", verifier;
"oauth_version", "1.0"]
let signingString = baseString "POST" accessTokenURI queryParameters
let oauth_signature = hmacsha1 signingKey signingString
let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters
let req = WebRequest.Create(accessTokenURI, Method="POST")
let headerValue = createAuthorizeHeader realQueryParameters
req.Headers.Add(HttpRequestHeader.Authorization, headerValue)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
let txt = (new StreamReader(stream)).ReadToEnd()
let parts = txt.Split('&')
(parts.[0].Split('=').[1],
parts.[1].Split('=').[1])
/// Compute the 'Authorization' header for the given request data
let authHeaderAfterAuthenticated url httpMethod token tokenSecret queryParams =
let signingKey = compositeSigningKey consumerSecret tokenSecret
let queryParameters =
["oauth_consumer_key", consumerKey;
"oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
"oauth_signature_method", "HMAC-SHA1";
"oauth_token", token;
"oauth_timestamp", currentUnixTime().ToString();
"oauth_version", "1.0"]
let signingQueryParameters =
List.append queryParameters queryParams
let signingString = baseString httpMethod url signingQueryParameters
let oauth_signature = hmacsha1 signingKey signingString
let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters
let headerValue = createAuthorizeHeader realQueryParameters
headerValue
/// Add an Authorization header to an existing WebRequest
let addAuthHeaderForUser (webRequest : WebRequest) token tokenSecret queryParams =
let url = webRequest.RequestUri.ToString()
let httpMethod = webRequest.Method
let header = authHeaderAfterAuthenticated url httpMethod token tokenSecret queryParams
webRequest.Headers.Add(HttpRequestHeader.Authorization, header)
type System.Net.WebRequest with
/// Add an Authorization header to the WebRequest for the provided user authorization tokens and query parameters
member this.AddOAuthHeader(userToken, userTokenSecret, queryParams) =
addAuthHeaderForUser this userToken userTokenSecret queryParams
let testing() =
// Compute URL to send user to to allow our app to connect with their credentials,
// then open the browser to have them accept
let oauth_token'', oauth_token_secret'', oauth_callback_confirmed = requestToken()
let url = authorizeURI + "?oauth_token=" + oauth_token''
System.Diagnostics.Process.Start("iexplore.exe", url)
// *******NOTE********:
// Get the 7 digit number from the web page, pass it to the function below to get oauth_token
// Sample result if things go okay:
// val oauth_token_secret' : string = "9e571e13-d054-44e6-956a-415ab3ee6d23"
// val oauth_token' : string = "044da520-0edc-4083-a061-74e115712b61"
let oauth_token, oauth_token_secret = accessToken oauth_token'' oauth_token_secret'' ("78846")
// Test 1: Get your profile details
let streamSampleUrl2 = "http://api.linkedin.com/v1/people/~"
let req = WebRequest.Create(streamSampleUrl2)
req.AddOAuthHeader(oauth_token, oauth_token_secret, [])
let resp = req.GetResponse()
let strm = resp.GetResponseStream()
let text = (new StreamReader(strm)).ReadToEnd()
text
// Test 2: Get a connections list
let streamSampleUrl3 = "http://api.linkedin.com/v1/people/~/connections"
let req = WebRequest.Create(streamSampleUrl3)
req.AddOAuthHeader(oauth_token, oauth_token_secret, [])
let resp = req.GetResponse()
let strm = resp.GetResponseStream()
let text = (new StreamReader(strm)).ReadToEnd()
text
Navigation hierarchies and key filters with slide library : Code Fix.
According to the popularity of the blog post : “Navigation hierarchies and key filters with slide library“, I decided to share a code that fix Slide Library view. This code snippet replace existing ListViewWebPart on the slide library view to the new XsltListViewWebPart and configure it in a proper way.
If you execute this code on the all slide library views then you will get a full-functional slide library with fixed navigation and filters.
public bool ReplaceListViewWebPartForView(SPWeb web, SPList list, SPView view)
{
using (SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager(view.Url, PersonalizationScope.Shared))
{
SPLimitedWebPartCollection pageWebParts = webPartManager.WebParts;
ListViewWebPart listViewWebPart =
pageWebParts.OfType<ListViewWebPart>().FirstOrDefault();
if (listViewWebPart == null || listViewWebPart.IsClosed) return false;
var xsltListViewWebPart =
new XsltListViewWebPart
{
ListName = listViewWebPart.ListName,
ViewFlags = listViewWebPart.ViewFlags,
ViewId = listViewWebPart.ViewId,
ViewGuid = listViewWebPart.ViewGuid,
Visible = listViewWebPart.Visible,
DisplayName = view.Title,
Default = view.DefaultView.ToString().ToUpper(),
WebId = listViewWebPart.WebId,
NoDefaultStyle = listViewWebPart.UseDefaultStyles.ToString().ToUpper()
};
//create new Webpart and set parameters from existing ListViewWebPart
webPartManager.AddWebPart(xsltListViewWebPart, listViewWebPart.ZoneID, 0);
//fix ViewFlags since SP added "Hidden" flag
xsltListViewWebPart.ViewFlags = listViewWebPart.ViewFlags;
webPartManager.DeleteWebPart(listViewWebPart);
webPartManager.SaveChanges(xsltListViewWebPart);
list.Update();
web.Update();
//get updated list and fix issue with empty title to allow View be visible on the site
SPList list1 = web.Lists[list.ID];
SPView view1 = list1.Views[new Guid(xsltListViewWebPart.ViewGuid)];
view1.Title = view.Title;
view1.DefaultView = view.DefaultView;
view1.MobileDefaultView = view.MobileDefaultView;
view1.MobileView = view.MobileView;
view1.Update();
web.Update();
return true;
}
}
P.S. Great thanks to the Dmitry Chirun for this fix.
