All text and code copyright (c) 2016 by David Podhola. Used with permission.
Original post dated 2016-12-22 available at http://www.naseukoly.cz/blog/2016/12-20-hello-fable-import-sharepoint/index.html
By David Podhola
This post is about 3 month long journey to fable-import-sharepoint. It is part of F# Advent 2016. Includes also a lot of information from my past.
If you want to ask questions, report issues or contribute, please feel free to at the fable-import-sharepoint GitHub repository.
Two months ago I became a part of a project team assigned a very interesting task - to rewrite a highly customized DMS on a top of Microsoft SharePoint. I was not for the first time I was writing for SharePoint - in fact, it was my fourth project this year, but this time it was a little bit special. The projects we wrote during the summer and autumn were provider-hosted SharePoint Add-ins. We wrote them as server applications running on the top of Suave hosted in IIS with the front end using React and Redux. These are the technologies we have been using for years and were proven to work for us and help us to write code faster.
SharePoint Designer Only
The new project was completely different. From all the SharePoint integration and customization options, only those accessible using SharePoint Designer were allowed. At the same time the user requirements were quite high which was understandable taking into the account they were used to the application created directly for their needs. Most of them were related to the user experience in terms of:
- information they see on the screen (add or remove)
- actions (links, buttons, menu items) accessible on the particular screen (add or remove)
- specialized validation (file name including wild cards against a specific list)
- multiple file upload
- cascade workflows with App-Steps
- very strict user permissions (e.g. upload only, cannot read the file later)
It was clear soon that not only heavy UI changes will need to be done, but also iterating for list items, starting workflows and so on.
We were very much influenced by that time latest news from Microsoft like SQL XML features so we decided most of the logic would be done by the SQL server generating XML that was later transformed by static XSLT files into the final XHTML.
For the developers with the Delphi, Visual Basic and later C# background with static types and the compile step checking for typos etc. it was simply a nightmare. Endless nights when we were trying to hunt bugs strange bugs in Internet Explorer. All the funny stories about comparing apples to oranges and getting the most difficult-to-understand results you can imagine.
About ten years later we were writing a ASP.NET MVC application for call centre agents build on the top of Microsoft Dynamics CRM. With the help of jQuery the code was much more readable now, but we still have to fight a lot of bugs in the runtime (and even production as part of the application was dynamically generated!). Not nice.
Heard about something called Angular at that time, but the server side generated HTML approach was still too strong.
Fast forward to mid-2015. We are writing an application for managing the front office clerks and their shifts. We decided to use ASP.NET Core and Angular. Had a lot of fun and although I did mostly the back end, but it looked like Angular is very usable and ready to replace the ASP.NET MVC.
When 2016 kicked in, I am helping my friends to add new Angular front end to their ASP.NET WebPages application from late 2000s. It is not my first Angular application, but for the first time (and definitely not for the last) we were adding Angular SPA to server generated application already many years in production.
F# and FunScript
I have described my path to programming in F# several times. For the past years me and my colleagues have been using F# for:
- writing simple console application
- writing Outlook AddIns
- writing ASP.NET web servers
Fable to the rescue
I was already aware Ionide is written in F# and uses Fable, but I needed to meet project where I would really use it.
- using Visual Studio Code with Ionide and the autocomplete not to have to remember all the SharePoint functions by heart
- creating the Domain Specific Language for the general SharePoint frontend development
- creating specific types and functions related to the project domain (complex DMS with specific workflows and strict permission system)
The first two from the list are shared in the fable-import-sharepoint GitHub repository.
Let me explain, how I created them and what they can be used to.
These are the files that help the autocomplete (IntelliSense) provided by Ionide in Visual Studio Core. They were created by running slightly modified
ts2fable on files from DefinitelyTyped/SharePoint and then working with the files by hand till the results were what I wanted them to be.
Looks boring? Was not at all!
ts2fable creates most of the content you need and also in this task creativity is needed.
The interface is far from perfect now, but was usable not only in the initial project, but also in all of the following.
You can use it to write the classic SharePoint snippets like
let clientContext = SP.ClientContext.get_current() let web = clientContext.get_web() clientContext.load(web)
Browser.Support.fs to make it easier to write jQuery calls in the functional way.
Literals.Global.Ribbon.ApproveReject |> el |> hide
Literals.Global.Ribbon.S4RibbonRow |> el |> find ( Literals.Global.Ribbon.MsCuiCtlMedium ) |> last |> hide
Literals.AccessRequestRPR.RequestType.ID |> el |> change ( fun () -> showAndHide() ) |> ignore
Note: this is not related to SharePoint development.
The requirements to change SharePoint user interface on multiple sites led to creating few small support functions in
HSharp.fs. If you implement
IApplication interface, you can then add
to your MasterPage.
render is called once time after the page is rendered when all the page is loaded onto the browser. It can be used to hide or show page UI elements, start workflows etc.
scheduled is scheduled to be called once per a second; can be used to e.g. dynamically change the user interface of the ribbon etc.