Create custom SharePoint Health Analyzer rules

Have you got a SharePoint farm that has a unique set-up, special monitoring requirements, particular SLAs that it must meet or a farm that needs to provide your operations team with pro-active monitoring. If so, create your own SharePoint Health Analyzer rules, it’s super easy!

I’m sure we’ve all worked on deployments that fall into one or more of the categories above or have tons of other requirements than would benefit from monitoring. Perhaps the monitoring you need is nothing to do with the farm deployment and it’s operating environment but instead monitoring of a custom application you’ve built. Either way, creating your own SharePoint Health Analyzer rules could be a good idea.

Here’s how you create them…

Start a new Visual Studio 2010 Empty SharePoint Project and add to it a new class. The class must inherit from SPHealthAnalysisRule:

image

Next, you need to override the Category and ErrorLevel your rule will be reported under:

image

Next, override the Explanation, Remedy and Summary strings the rule returns. These are what the user see when the rule is displayed in the Review problems and solutions list from within Central Administration.

image

Next, override the SPHealthAnalysisRuleAutomaticExecutionParameters, these control how, where and when the rule is checked.

image

The interesting option here is the Scope. The Scope allows the rule to be executed on ‘Any’ or ‘All’ servers in the farm. Depending on what your rule is designed to do, running it on one server might be enough but you may need to run it on every server. For example, a health rule than check the size of a content database could be run on any server (SPHealthCheckScope.Any) as it doesn’t matter from which server you interrogate your database for its size. However, a rule that checks for available disk space will need to be executed on every server (SPHealthCheckScope.All).

Now the last part, the rule logic itself. To implement this, simple override the Check() method:

image

The check method must return a SPHealthCheckStatus:

image

As you can see, creating rules is simple. Installing the rules is a little more involved but still only a few lines of code. To deploy the rules, you’ll need to add a farm scoped feature to your project, add to the farm scoped feature an event receiver and override the FeatureActivated and FeatureDeactivating events. The FeatureActivated event will install the rules contained in the assembly produced by the project by calling the RegisterRules method of the SPHealthAnalyzer class:

image

Lastly, the code to remove the rules on feature activation is just as simple:

image

Now deploy your feature and watch it fail…

There’s on last trick to getting this working. It appears that there is a issue with deploying the solution and activating the feature all in one step (just like Visual Studio tries to do). The RegisterRules method call fails if you attempt this, I suspect this is due to timing of the DLL becoming available in the GAC but I haven’t got to the bottom of this one yet. To work around this issue, update the farm feature manifest.xml to include the ActivateOnDefault=”False” attribute:

image

Now you can deploy your solution, manually activate your farm feature and begin testing your custom rules.Smile

If you want a complete sample solution that includes the rule I’ve used as an example in this post and many more you can download the source code and WSP at http://sphealth.codeplex.com

Enjoy.

Silverlight, JavaScript and SharePoint Modal Dialogs

I spent some time recently using Silverlight and SharePoint modal dialogs in anger for a client engagement. For this particular project we’re dealing with lots of hierarchical data structures and the function requirement is to provide a drag and drop user interface to support building and reorganising these hierarchies. When combined with the platform choice of SharePoint, Silverlight seemed to be the obvious technology with which to build these controls. We’re using the drag and drop examples form the Silverlight Toolkit for the basis of our controls and combining them with the SharePoint modal dialog framework via SP.UI.ModalDialog.showModalDialog.

To support the solution, we need to create lots of modal dialogs in SharePoint and test the Silverlight controls functionality within these modals. To help support this testing, I built a SharePoint web part that is used to launch modal dialogs:

image

The web part itself, renders a Silverlight control that is used to capture the options needed to launch the modal. When the ‘Go’ button is clicked, the Silverlight control calls the required JavaScript function that launches the modal. The modal is displayed and once it is closed, the results of the modal are returned to the Callback function. In turn, the Callback function calls a ScriptableMember method in the Silverlight control and the results of the modal are then displayed in the Callback Result Value textbox of the Silverlight control.

The flow of data this web part provides is as follows:

Web part  >  Silverlight  >  JavaScript  >  SP.UI.ModalDialog  >  JavaScript  >  Silverlight

There is however, one big gotcha with this data flow that tripped me up for quite a while. When control is passed back to the Callback function after the modal has been closed, the Silverlight control appears to be reloaded. When this happens, any data or settings your entered into the web part are lost and the control reloads with its initial configuration. Clearly this is far from ideal…

I noticed however, that this behaviour is not replicated in Firefox. With Firefox, the results are passed back to the JavaScript Callback function, the function calls the ScriptableMember method without reloading the Silverlight control and all of our settings are preserved plus the results from the modal are shown to the user as per our intended behaviour.

This observation led me to this post that describes an issue with Silverlight and IE that causes the Silverlight plugin to be unloaded and reloaded when a Silverlight control is hidden that currently has focus. The solution suggested by this post is to set focus to another object in the DOM prior to hiding the object. Hiding all the object tags on the calling page is precisely what the SharePoint modal dialog framework does – thus it causes the Silverlight control to be unloaded and reloaded.

The following additional line of JavaScript added just prior to launching the modal dialog solves our problem when this technique is used with IE:

image

The source code for the web part and Silverlight control are available on codeplex: http://throwamodal.codeplex.com/

I hope this helps…

Pass event data from a Silverlight control to a SharePoint page

There are many examples out there of how to pass data from a SharePoint page or web part to a Silverlight control. For example, you could use the an HTML bridge, initParams or even the client object model -see How to pass data to Silverlight control for loads of examples.

However, I could not find an example of how to pass data from Silverlight to SharePoint that also dealt with SP.UI.ModalDialog’s so this post will describe what I came up with.

Note: This post is not about how to fetch data to and from Silverlight via the object model. What I’m attempting to describe here is how an event in a Silverlight control can be used to pass information back to SharePoint via a Modal Dialog.

The example scenario I’m using is a follows: you have a source SharePoint page or web part (blue) that needs to call a Silverlight control to perform some advanced function. The source page wants to open the Silverlight control in an SP.UI.ModalDialog. The target of the modal dialog is another SharePoint page or web part (green). The target page in turn renders the Silverlight control (red).

image

Here are the scenarios I’m trying to answer:

  • How does the Silverlight control close the modal dialog that it is contained within?
  • How does the Silverlight control pass data back to the source page?
    To answer these questions, changes are required to be made to the source page, the target page and the Silverlight control. However, these changes are fairly simple to apply and the results work seamlessly.

    Source page changes

    We need to update the source page to include the necessary JavaScript to open an SP.UI.ModalDialog and to respond to its close event. I’m assuming you’ve got access to the code behind for the source page, if not, all the code shown can be place into a web part that is then placed on the source page or (a version of) the JavaScript can be directly added to the source page.

    First we create a variable to hold the unique name for our source page script:
    // create a unique name for our JavaScript
    string scriptName = "ThrowAModal" + DateTime.Now.Ticks.ToString();

    Next we start a new StringBuilder and write the open tag for our script element:

    // construct the required script contents
    StringBuilder modal = new StringBuilder();
    modal.Append("<script language='javascript' type='text/javascript'>");

    Next we create the JavaScript function that will get called when our ModalDialog is finally closed – this is our CallBack function. In this example I’m testing the results of the dialog (e.g. was it cancelled or closed gracefully) and then I’m displaying the returnValue than will eventually come from the Silverlight control in a notification message. When you see the notification message you’ll know that the Silverlight control has successfully passed data back to the source page:

// this will get called when the modal is closed
#region CallBack Javascript
modal.Append("function " + scriptName + "CallBack(dialogResult, returnValue)");
modal.Append("{");
modal.Append(" alert(dialogResult + ' - ' + returnValue); ");
modal.Append(" if(dialogResult==1) ");
modal.Append("  {");
modal.Append("  var myNotifyId = SP.UI.Notify.addNotification(returnValue, false);");
modal.Append("  }");
modal.Append("}");
#endregion

Next we create the JavaScript that opens our target page in a ModalDialog. What’s important to note here is that were passing into the options for the ModalDialog, the name of our CallBack function we defined above:

// this will open the modal
#region Dialog Javascript
modal.Append("function " + scriptName + "() { ");
modal.Append("var options = { ");
modal.Append("url: '/yourURLhere.aspx', ");
modal.Append("width: 800, ");
modal.Append("height: 500, ");
modal.Append("title: 'Throw a Modal sample', ");
modal.Append("allowMaximize: false, ");
modal.Append("showClose: true, ");
modal.Append("dialogReturnValueCallback: " + scriptName + "CallBack");
modal.Append("};");
modal.Append("SP.UI.ModalDialog.showModalDialog(options); ");
modal.Append("}");

Next we complete our script by actually calling our function that will open the ModalDialog:

// cause the modal to be opened 
modal.Append("ExecuteOrDelayUntilScriptLoaded(" + scriptName + ", \"sp.js\"); ;");
#endregion

Finally, our source page JavaScript is complete so we can close the script block and add the script to the page:

modal.Append("</script>");

// add the script to the page
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), scriptName, modal.ToString());

Target page changes

Now we need to make a minor change to our target page. Again, this could be done in the code behind, in a web part or by adding the JavaScript directly into the page.

First we use a StringBuilder to create our script block:

// construct the required script contents
StringBuilder targetScript = new StringBuilder();
targetScript.Append("<script language='javascript' type='text/javascript'>");

Next we create the function that our Silverlight will be calling and that in turn will close the ModalDialog and pass the results from Silverlight to the source page CallBack function:

// this will be called by Silverlight
#region Invoke Javascript
targetScript.Append("function CancelPressed(results)");
targetScript.Append("{");
// show the data passed out of Silverlight
targetScript.Append(" alert(results);"); 
// close the dialog and return the results
targetScript.Append(" window.frameElement.commonModalDialogClose(1,results);");  
targetScript.Append("}");
#endregion

Notice the above function is called ‘CancelPressed’ we’ll need to reference this script by name in our Silverlight control.

We’re done with the target page scripting so we can close our script block and add the script to our page:

targetScript.Append("</script>");

// add the script to the page
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CancelPressedScript", targetScript.ToString() );

Silverlight control changes

The changes required to be made to the Silverlight control are the simplest of all. On the event you want to use to trigger closing the ModalDialog (and with it the Silverlight control) and pass data back to the source page CallBack function simply add the following code:

private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
    // call our target page function
    System.Windows.Browser.HtmlPage.Window.Invoke("CancelPressed", 
        "Hello from Silverlight - cancel has just been pressed.");
}

The System.Windows.Browser.HtmlPage.Window.Invoke method takes two parameters. The first is the name of the script object we wish to call. This needs to be the name of the function you added to the target page. The second parameter is a params object[] array that can be used to send data back to the target page and onto the source page. In this example I’m simply passing a string message but this same technique will work for all primitive data types.

Result

Here the overall process flow the above code helps achieve:

image

I hope this helps…

Programmatically enable Ajax Auto Refresh on a SharePoint XsltListViewWebPart

On of the capabilities of the XsltListViewWebPart used extensively by SharePoint 2010 is the automatic refreshing of list view data via Ajax. Used judiciously, this can be very useful for creating dynamic views of data, pseudo-dashboards or in my case, providing feedback of timer job processing via the UI.

To enable automatic refresh via the UI is simple, edit the web part properties, and switch on the ‘Enable Asynchronous Automatic Refresh’, set the refreshing interval (which must be no more frequent than 15 seconds) and save the web part.

image 

To achieve the same results via code is just as simple:

// update the XsltListViewWebPart to refresh automatically via Ajax
// get the webpart page and webpart manager
SPFile file = web.GetFile("Lists/<your list name>/AllItems.aspx");
SPLimitedWebPartManager wpManager = 
    file.GetLimitedWebPartManager(PersonalizationScope.Shared);

// get the XsltListViewWebPart...
// assumes the XsltListViewWebPart is the first webpart on the page
XsltListViewWebPart lvwp = (XsltListViewWebPart)wpManager.WebParts[0];
                
// set the webpart to autorefresh and set the interval
lvwp.AutoRefresh = true;
lvwp.AutoRefreshInterval = 15;

// save the changes
wpManager.SaveChanges(lvwp);

Enjoy…

SharePoint Lorem Ipsum Web Part to display random content

One of the things I like to do when I create SharePoint page layouts is to test them with different content to ensure the layout is robust enough to handle both short content blocks and very long content blocks. Typically layout issues can arise within or around floating DIVs, with page scrolling or overflowed content so its important these (and many other) facets are tested thoroughly.

One of the tools I use to test these page layouts is my Lorem Ipsum web part. The web part uses JavaScript to build a content block of random length. The web part uses no managed code (it is packaged as a dwp web part file) so can be used in sandboxed environments and is simply uploaded via the Add Web Part UI:

image

Once the web part has been uploaded via the ‘Upload a Web Part’ option, it will appear in the Imported Web Parts category. From here you can add it to the page like any other web part:

image

Now with the web part on page, every time the page is refreshed, the amount of content displayed by the web part will change:

image

The maximum number of words that the web part can choose to display can be set by updating the numOfWords value in the script block that is contained inside the dwp web part file.

image

Here’s the complete contents of the dwp web part file, alternatively, you can download the file from the link below:

<?xml version="1.0" encoding="utf-8"?>
<WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">
  <Title>Lorem Ipsum Content</Title>
  <FrameType>Default</FrameType>
  <Description>Use to display random content to aid testing page layouts</Description>
  <IsIncluded>true</IsIncluded>
  <ZoneID>wpz</ZoneID>
  <PartOrder>0</PartOrder>
  <FrameState>Normal</FrameState>
  <Height />
  <Width />
  <AllowRemove>true</AllowRemove>
  <AllowZoneChange>true</AllowZoneChange>
  <AllowMinimize>true</AllowMinimize>
  <AllowConnect>true</AllowConnect>
  <AllowEdit>true</AllowEdit>
  <AllowHide>true</AllowHide>
  <IsVisible>true</IsVisible>
  <DetailLink />
  <HelpLink />
  <HelpMode>Modeless</HelpMode>
  <Dir>Default</Dir>
  <PartImageSmall />
  <MissingAssembly>Cannot import this Web Part.</MissingAssembly>
  <PartImageLarge>/_layouts/images/mscontl.gif</PartImageLarge>
  <IsIncludedFilter />
  <Assembly>Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
  <TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>
  <ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
  <Content xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor"><![CDATA[<script>

  // setup our array of available text
  var source = new Array();
  source[0] = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.";
  source[1] = "Epsum factorial non deposit quid pro quo hic escorol. Olypian quarrels et gorilla congolium sic ad nauseum. Souvlaki ignitus carborundum e pluribus unum. Defacto lingo est igpay atinlay. Marquee selectus non provisio incongruous feline nolo contendre. Gratuitous octopus niacin, sodium glutimate. Quote meon an estimate et non interruptus stadium. Sic tempus fugit esperanto hiccup estrogen. Glorious baklava ex librus hup hey ad infinitum. Non sequitur condominium facile et geranium incognito. Epsum factorial non deposit quid pro quo hic escorol. Marquee selectus non provisio incongruous feline nolo contendre Olypian quarrels et gorilla congolium sic ad nauseum. Souvlaki ignitus carborundum e pluribus unum.";
  source[2] = "Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc., li tot Europa usa li sam vocabularium. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilit? de un nov lingua franca: on refusa continuar payar custosi traductores. It solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.";
  source[3] = "Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental: in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es."

  // pick the starting point for our random text
  var text_no = Math.floor((4)*Math.random());

  // setup our array of available text
  var lorem = new Array();
  switch(text_no)
      {
              case 0: {
            lorem[0] = source[0] + source[1] + source[2] + source[3]
            break;
            }
          case 1: {
            lorem[0] = source[1] + source[2] + source[3] + source[0]
               break;
            }
          case 2: {
            lorem[0] = source[2] + source[3] + source[0] + source[1]
               break;
            }
          case 3: {
            lorem[0] = source[3] + source[0] + source[1] + source[2]
               break;
            }
    }

  // pick the number of words
  var numOfWords = Math.floor((500)*Math.random()) + 20;

  var list = new Array();
  var wordList = new Array();
  wordList = lorem[0].split( ‘ ‘ );
  var iParagraphCount = 0;
  var iWordCount = 0;

  while( list.length < numOfWords )
    {
         if( iWordCount > wordList.length )
            {
                iWordCount = 0;
                iParagraphCount++;
                if( iParagraphCount + 1 > lorem.length ) iParagraphCount = 0;
                wordList = lorem[ iParagraphCount ].split( ‘ ‘ );
                wordList[0] = "<br/><br/>" + wordList[ 0 ];
            }
        list.push( wordList[ iWordCount ] );
        iWordCount++;
    }
 
  var out_str = list.join(‘ ‘)
  out_str = out_str + "…"

  document.write(out_str);

</script>]]></Content>
  <PartStorage xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
</WebPart>

download The dwp web part file used in this post can be downloaded from here: https://skydrive.live.com/redir.aspx?cid=941d17eca8c6632d&resid=941D17ECA8C6632D!378&parid=941D17ECA8C6632D!376

The JavaScript used in the web part contains portions of a JavaScriptBank script that can be found here: http://www.javascriptbank.com/javascript/utility/generator/random-text-generator/print/en/

Enjoy…

Insert Excel REST chart into Word documents–an alternative approach

The most frequently documented way to embed an Excel chart object into a Word document via the Excel REST API is to insert into the Word document a new Quick Part via the Insert > Quick Parts > Field… > IncludePicture command and ensure the ‘Data not stored with document’ option is ticked. This option ensures that the chart is dynamically refreshed each time the Word document is opened:

image

However, there is I believe a better way to achieve the same (and arguably a more usable) result in a much more user friendly and demo friendly manner. Rather than use the Insert > Quick Parts > Field… > IncludePicture option, insert the picture from the standard Insert > Picture option:

image 

Here’s the important step – don’t simply click on the Insert button. Instead, from the Insert button drop down, select Insert and Link:

image

When you do this, your Excel chart is dynamically fetched and inserted into the Word document as you would expect.

But here’s the bonus…

If you use the first method, you need to close and reopen the Word document for the chart to refresh or wait several minutes and then press Ctrl+A, F9 to refresh the embedded chart – in my experience you have to wait 5 to 10 minutes, and sometimes even longer, for the chart to refresh. Consequently it’s usually quicker to close and reopen the Word document.

However, if you use the second method to embed a chart into your Word document, Ctrl+A, F9 updates the chart immediately. Not only does this method make the insertion of charts look more elegant but you can demonstrate dynamic data refreshes without have to shutdown and reopen Word.

Happy demoing….

Bill Gates lives in all our SharePoint installations

Here’s something I learnt from an Office 365 presentation by Mark Kashman I watched today. Bill Gates is alive and well and living in all our SharePoint installations!

You know the placeholder image for a user profile, this one:

image

Well it transpires that it is based on Bill Gates mugshot:

Here’s the ‘proof’…

image

Enjoy…