Subscribe to News feed

Microsoft Giveth and Taketh, the mystery of the SPAuditQuery.RowLimit property

Posted at: 14:34 on 26 August 2009 by Muhimbi

gift

During a recent optimisation cycle of our Audit Log Viewer (Part of the Muhimbi SharePoint Audit Suite) we noticed the RowLimit property on the SPAuditQuery object. Weird, not sure why we missed this the first time around, but we’ll gladly accept this gift and move on.

The thing is, some of our customers just blindly enable all audit events on all of their site collections and never look back. The record stands, I kid you not, at 848 Million lines of audit data in a single table. As our audit software behaves like a good SharePoint citizen, we access all data through the SharePoint object model and never touch the database directly.

Unfortunately, if a user queries the audit log and removes all filters, the SharePoint object model retrieves all data and happily converts each and every line of audit data into an SPAuditEntry object. This is a relatively slow process that cannot be interrupted and may result in extreme memory and cpu usage.

OK, so back to this shiny RowLimit property. Our log viewer already contained some optimisations to deal with large amounts of data, but being able to set the RowLimit is going to solve the problems of even our most demanding customers….. excellent.

Excellent indeed, everything was working great in our development environment, but during a regression test one of our Test farms was complaining and threw an Exception related to get_RowLimit() not being found…. bugger! We rubbed our eyes, fired up Reflector and compared Microsoft.SharePoint.dll from our Test environment with the one in our Development environment.

RowLimit

As is evident in the screenshot listed above, the version running in our Development environment (on the right) has public members that are not present in the version running in our Test Environment (on the left).

It appears that Microsoft has not documented on which version of the DLL the RowLimit property was introduced (12.0.6219.1000 doesn’t have it, but 12.0.6318.5000 does) so we have decided to detect the availability of the property at runtime to allow our software to auto optimise on systems running the newer version of the DLL.

The code is split up in two methods as the actual line using the RowLimit property cannot be in the same method that is testing its presence. This is related to how .net’s JIT compiler works in the background.

/// <summary>
/// Check presence of RowLimit and set the value
/// </summary>
/// <param name="wssQuery">The query to set the limit on.</param>
public static void SetRowLimit(SPAuditQuery wssQuery)
{
    Type t = typeof(Microsoft.SharePoint.SPAuditQuery);
    PropertyInfo p = t.GetProperty("RowLimit");
 
    if (p != null)
    {
        SetRowLimitInternal(wssQuery);
    }
}
 
/// <summary>
/// Internal method for setting the actual row limit. In its own method
/// to prevent the JIT from tripping over in the SetRowLimit method above.
/// </summary>
/// <param name="wssQuery">The query to set the limit on.</param>
private static void SetRowLimitInternal(SPAuditQuery wssQuery)
{
    wssQuery.RowLimit = MAX_QUERY_ROWS;
}





Labels: , ,

New release of the Muhimbi SharePoint Audit Suite v 1.1.0.0

Posted at: 18:00 on 25 August 2009 by Muhimbi

SharePointAuditBox We are proud to announce the availability of the SharePoint Audit Suite version 1.1.0.0. Although this version adds a number of new features, the main focus has been on improving existing functionality, fixing bugs and improving scalability.

For those not familiar with the product we recommend reading the original announcement. In summary, the Muhimbi SharePoint Audit Suite is a suite of tools that allow auditing to be enabled automatically on new and existing Site Collections, Audit Logs to be viewed using a user friendly viewer and Auditing to be monitored using an Audit Monitor. The software runs on both WSS 3 as well as MOSS.

The key changes and improvements are as follows:

563 New: Improved Excel Exporter
- Now exports in ExcelML format, compatible with Excel 2002/2007.
- Enables Excel’s Auto filtering by default to allow further filtering.
- Print area automatically updated to fit all audit entries.
- Page layout optimised to make the width of the table fit on a single page
478 New: File Audit Log Viewer - Users with full control access on a list or library should always be allowed to open the audit viewer for individual files.
137 New: Add page to website to describe all audit events and link to it from the GUI.
427 New: Audit Log Viewers - Add Login ID to tooltip when hovering over user’s name.
623 New: Increase number of characters in Description column to 300.
517 Bug Fix: An SPRequest object was not disposed before the end of this thread. This was caused by an undocumented issue of the Site Collection picker. Read this blog posting for details.
475 Bug Fix: File Audit Log Viewer - Cannot view audit entries before file is checked in for the first time.
514 Bug Fix: Farm Audit Settings - It appears that Audit Mask is set multiple times when applying a new one.
471 Bug Fix: File Audit Viewer - Clicking 'Cancel' opens the wrong page.
474 Bug Fix: Doclib Audit Viewer - Clicking 'Cancel' opens the wrong page
512 Bug Fix: Audit Log Viewers - Selected Audit Events not remembered after validation error.
432 Bug Fix: Audit Log Viewers - Clicking Export to Excel does not work unless 'View results' is clicked first.
426 Bug Fix: Exporting the file to Excel results in a warning message.
461 Bug Fix: Excel Export - Seconds are not displayed in the timestamp.


AuditLogViewer

For more information check out the:

As always, feel free to contact us using Twitter, our Blog or regular email or subscribe to our newsletter.

Download your free trial here (1.5MB). You don’t even have to register, but we would appreciate it if you did.

Labels: , ,

Adding UserVoice to your SharePoint site the easy way

Posted at: 09:55 on 21 August 2009 by Muhimbi

We really love these free ‘Web 2.0’ kind of services. You add a line or 2 of pre-generated code to your site and BANG, you got yourself a ‘reteweet button’ (see below) or a full blown user forum. Today we’ll show how to add one of those cool ‘Feedback buttons’ that links to UserVoice’s fancy suggestion box service. 

To ensure this new functionality is added to every page in the Site Collection we use our free SharePoint Infuser rather than the Content Editor Web Part. No messing about in SharePoint Designer, modifying Master Pages or dealing with ‘Ghosting’ issues.

As part of this example we will sign up to a free UserVoice account, but if you just want to play around you can use our test account details embedded in the JavaScript code below.

UserVoiceScreens Notice the black feedback button in the left screen and the UserVoice overlay in the right one.


Follow the steps outlined below to integrate UserVoice in your site collection:

  1. Download and install Muhimbi’s SharePoint Infuser on one of your Web Front End Servers.
     
  2. Ensure you have Designer privileges, more specifically the Add and Customize Pages right.
     
  3. Sign up for a free UserVoice account. (Or use our demo account details below).
     
  4. In the UserVoice Administration screen, navigate to the ‘Widgets’ tab and copy the JavaScript code to your clipboard. (Or use the demo code at the end of this post)
     
  5. In your SharePoint Site collection, on the Site Actions / Site Settings screen select Infuse custom content from the Look and Feel column.
     
  6. Paste the content of your clipboard into Infuser’s code window. If you are using IE then you may want to paste it in WordPad first , otherwise all line breaks are stripped out.
     
  7. Click the Save button and navigate to any page in the site collection

 

<script type="text/javascript">
    var uservoiceJsHost = ("https:" == document.location.protocol) ?
                          "https://uservoice.com" : "http://cdn.uservoice.com";
    document.write(unescape("%3Cscript src='" + uservoiceJsHost +
                          "/javascripts/widgets/tab.js' type='text/javascript'%3E%3C/script%3E"))
</script>
<script type="text/javascript">
    UserVoice.Tab.show({
        /* required */
        key: 'MuhimbiTest',
        host: 'MuhimbiTest.uservoice.com',
        forum: '26878',
        /* optional */
        alignment: 'right',
        background_color: '#000',
        text_color: 'white',
        hover_color: '#06C',
        lang: 'en'
    })
</script>

Labels: ,

Additional ‘little known’ SharePoint Dispose Guidelines

Posted at: 11:34 on 20 August 2009 by Muhimbi

dispose

If you have been a SharePoint developer for more than a day, it will come as no surprise that you need to take great care of manually disposing your objects. Much has been written on this subject, but there are still gaps in the SharePoint Community’s knowledge.

A couple of weeks ago we identified an undocumented requirement to call Dispose() on SPFile streams and today we are documenting a resource leak related to SharePoint’s fancy SiteAdministrationSelector class, which allows users of our Audit Suite to quickly switch between the audit logs for different Site Collections.

During our latest test cycle we were seeing the following line in our SharePoint Trace Logs every time our Audit Log Viewer was invoked, even when the SiteAdministrationSelector was hidden or disabled:

An SPRequest object was not disposed before the end of this thread.  To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.  Due to flags specified at object creation, this will not be freed until processed by garbage collection.  Allocation Id: {F4F8B307-1F2C-4925-9C23-2A1EBED4B475}  To determine where this object was allocated, create a registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings.  Then create a new DWORD named SPRequestStackTrace with the value 1 under this key."

As we are avid users of SPDisposeCheck we were pretty sure we were not leaking any obvious SPSite or SPWeb resources. We tried explicitly disposing the SiteAdministrationSelector in the OnUnload event, but no luck. After several more, and increasingly desperate, attempts we found out that the object that requires disposing is the CurrentItem property, leaving us with the following OnUnload event.
 

protected override void OnUnload(EventArgs e)
{
    try
    {
        // ** Dispose the selected site in the selector. See bug #373
        if(siteCollectionSelector.CurrentItem != null)
            siteCollectionSelector.CurrentItem.Dispose();                
 
        // ** Dispose our global objects
        if (_selectedSite != null && _disposeSelectedSite == true)
            _selectedSite.Dispose();
        if (_selectedWeb != null && _disposeSelectedWeb == true)
            _selectedWeb.Dispose();
    }
    catch (Exception ex)
    {
        ExceptionHelper.ProcessException(ex, this);
    }
}

 

Additional topics regarding the prevention of resource leaks in SharePoint are discussed in section 7.11 of our SharePoint development guidelines. Download your free copy here.






Labels: ,

Solution for layouts.sitemap not accessible for non privileged SharePoint users

Posted at: 12:18 on 19 August 2009 by Muhimbi

exception

At Muhimbi we take great pride in the fact that we actually deal with exceptions properly. And when I say properly I mean Not ignoring them, not letting them bubble to the top and make it the users’ problem, but rather catch them, enrich them, log them, take appropriate action and present the user with a friendly message. Oh… and LOG THEM! See, it is so important I mentioned it twice.

During a recent test cycle of our SharePoint Audit Suite, we noticed the following entry in the Event log. The weird thing is that it only happens if the particular Application page was the first page being requested since the Application Pool was last recycled, and only if that request was made by a non privileged (read not a development I can go anywhere administrator)  account.

* Message: The file _app_bin/layouts.sitemap required by XmlSiteMapProvider does not exist.
  - Exception: System.InvalidOperationException
  - StackTrace:    at System.Web.XmlSiteMapProvider.CheckSiteMapFileExists()
   at System.Web.XmlSiteMapProvider.GetConfigDocument()
   at System.Web.XmlSiteMapProvider.BuildSiteMap()
   at Microsoft.SharePoint.Navigation.SPXmlContentMapProvider.BuildSiteMap()
   at System.Web.XmlSiteMapProvider.get_RootNode()
   at Muhimbi.SharePoint.Audit.AuditLogViewer.provider_SiteMapResolve(Object sender, SiteMapResolveEventArgs e)

Fortunately, and surprisingly, the error message is quite descriptive. Our first hunch was that SharePoint is trying to load ‘_app_bin/layouts.sitemap’ using the current user’s credentials. After the first request is successful it caches the file for future requests, quite sensible.

To prove our hypothesis we fired up our favourite troubleshooting tool, Process Monitor (Thanks Mark!) and placed a filter on the file system to only return entries containing ‘.sitemap’. After making the first request it became clear that SharePoint is indeed using the current user’s account to access this file as is evident in the following screenshot.

 

SiteMapException

 

The solution, as it so often is, is to elevate the part of the code that causes the sitemap file to be read. in our case the following bit.

/// <summary>
/// Event handler that allows the breadcrumb to be modified.
/// </summary>
public SiteMapNode provider_SiteMapResolve(object sender, SiteMapResolveEventArgs e)
{
    try
    {
        // ** Irrelevant code removed for the sake of clarity
 
        // ** First request uses the current user's account to load sitemap file, so elevate.
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            SiteMapNode listNode = e.Provider.RootNode.ChildNodes[0].Clone();
            listNode.Url = url;
            listNode.Title = title;
            listNode.ChildNodes = new SiteMapNodeCollection();
 
            pageTitleNode = new SiteMapNode(e.Provider, Guid.NewGuid().ToString());
            pageTitleNode.Title = HttpContext.GetGlobalResourceObject("MuhimbiAuditSharedResources", 
"Page_Title_AuditLogViewer").ToString();
            listNode.ChildNodes.Add(pageTitleNode);
            pageTitleNode.ParentNode = listNode;
        });
    }
    catch (Exception ex)
    {
        ExceptionHelper.ProcessException(ex, this);
    }
 
    return pageTitleNode;
}

 

Another case…err…bug closed, download our full and free SharePoint Development Guidelines.






Labels: ,

How to Float the SharePoint Quick Launch menu with the location of the scroll bar

Posted at: 18:54 on 04 August 2009 by Muhimbi

Another day and another cool example of how the SharePoint user interface can be modified using our free SharePoint Infuser. Today we are using some JavaScript magic to make this carpet … errrr … menu float in the same position while scrolling through a page.

To ensure this new functionality is added to every page in the Site Collection we use Infuser rather than the Content Editor Web Part. The JavaScript code is relatively simple, all it does is hook the scroll event, check if the menu is about to scroll off-screen and then re adjust the ‘top’ of the menu with the position of the scroll bar. Some additional code inserts a place holder element in the space that used to be occupied by the menu to ensure the width of this area doesn’t collapse.

There are more elegant ways to achieve floating effects, but the advantage of this code is that it works in IE6,7,8, FireFox, and Google Chrome. (It really works brilliantly in Chrome, as smooth as it gets)
 

FloatingMenu Notice how the Quick Launch menu scrolls with the content


Follow the steps outlined below to implement the changes on your site collection:

  1. Download and install Muhimbi’s SharePoint Infuser on one of your Web Front End Servers.
     
  2. Ensure you have Designer privileges, more specifically the Add and Customize Pages right.
     
  3. From the Site Actions / Site Settings screen, select Infuse custom content in the Look and Feel column.
     
  4. Copy the script at the end of this posting and paste it into Infuser’s code window. If you are using IE then you may want to paste it in WordPad first , otherwise all line breaks are stripped out.
     
  5. Click the Save button and navigate to any page that is long enough to have a scroll bar and scroll the window down.
     

<!-- Load the JQuery Libraries that ship with Infuser -->
<script type="text/javascript" src="/_layouts/Muhimbi.Infuser/JQuery/jquery-1.3.2.min.js"></script>
 
<script type="text/javascript">
    /**********************************************************************
      Floating SharePoint Menu, compatible with IE6,7,8 Chrome, Firefox
      Created by www.muhimbi.com, This sample code is provided on an “as is”
      basis and without warranty of any kind.
    ***********************************************************************/
 
    var scrollMenuElement = null;
    var menuElementTop = 0;
    var menuElementWidth = 0;
 
    function scrollMenu()
    {
        // ** Initialise during the first scroll event
        if(scrollMenuElement == null)
        {
            // ** Get the container that holds the navigation menu(s)
            scrollMenuElement = $("#LeftNavigationAreaCell > table");
            menuElementTop = scrollMenuElement.position().top;           
 
            // ** Request and explicitly specify width for Firefox & Chrome
            menuElementWidth = scrollMenuElement.width();
            scrollMenuElement.css("width", menuElementWidth);
 
            // ** put content in the same size of the menu to prevent collapse
            scrollMenuElement.before("<div style='width:" + menuElementWidth + "px'></div>");
 
            // ** Switch to absolute positioning to allow floating         
            scrollMenuElement.css("position", "absolute");
        }
 
        var scrollTop = $(document).scrollTop();
        // ** Has the element scrolled out of the window?
        if(scrollTop > menuElementTop)
        {
            scrollMenuElement.css("top", scrollTop +"px");
        }
        else
        {
            // ** Sometimes we scroll in large chunks so make sure it lines up
            scrollMenuElement.css("top", menuElementTop +"px");
        }
    }
 
    jQuery.event.add(window, "scroll", scrollMenu);
</script>
 

.

Labels: , ,

Fixing SharePoint’s Wiki by adding a home button and repairing the breadcrumb

Posted at: 11:41 on by Muhimbi

loopback It has been less than a week since we released our free SharePoint Infuser and we are already at our third blog posting.

Today we are fixing something that has been bothering us for a long time, which is the lack of proper navigation in the SharePoint Wiki, particularly the lack of a ‘home’ button and the fact that the Breadcrumb navigates to allitems.aspx. Grrrrrr.

So, in order to fix this we use SharePoint Infuser to insert the appropriate JavaScript into every Wiki Page in one go. Similar workarounds have been available for a while, but they need to be applied to each wiki page separately, which isn’t a scalable solution.

We are using Chris Chapman’s article on how to add buttons to the Wiki Toolbar as a starting point, we then add some JQuery magic to locate the appropriate breadcrumb link and set its URL to the wiki home page in a way that works across different languages as this page is not always called ‘home.aspx’.

 WikiHome


Follow the steps outlined below to implement the changes on your Wiki:

  1. Download and install Muhimbi’s SharePoint Infuser on one of your Web Front End Servers.
     
  2. Ensure you have Designer privileges, more specifically the Add and Customize Pages right.
     
  3. From the Site Actions / Site Settings screen, select Infuse custom content in the Look and Feel column.
     
  4. Copy the script at the end of this posting and paste it into Infuser’s code window. If you are using IE then you may want to paste it in WordPad first , otherwise all line breaks are stripped out.
     
  5. Click the Save button and navigate to any Wiki page.
     

Note that the script below works without modification on a ‘Wiki Page Library’. In order to make it work on a ‘Wiki Site’ change the ‘wikiHome’ variable to ‘../’ or whatever the equivalent of ‘home.aspx’ is in your language.

<!-- Load the JQuery Libraries that ship with Infuser -->
<script type="text/javascript" src="/_layouts/Muhimbi.Infuser/JQuery/jquery-1.3.2.min.js"></script>
 
<script type="text/javascript">
    function addWikiHomeButton()
    {
        // ** Are we on a wiki page?
        if($("table.ms-wikitoolbar").length > 0)
        {
            // Wiki home page that works across multiple languages
            var wikiHome = ".";
 
            // Fix the breadcrumb title
            $("span[id$=PlaceHolderTitleBreadcrumb_ContentMap] a:last").attr("href", wikiHome);
 
            // make copies of the layout cells for the wiki toolbar
            var $tdSeparator = $("table.ms-wikitoolbar tbody tr td.ms-separator:first").clone();
            var $tdToolbar = $("table.ms-wikitoolbar tbody tr td.ms-toolbar:first").clone();
            var $tdLastCell = $("table.ms-wikitoolbar tbody tr td.ms-toolbar[width]:last").clone();
            var $parent = $("table.ms-wikitoolbar tbody tr td.ms-toolbar[width]:last").parent();
 
            // remove the last TD element (padding)
            $("table.ms-wikitoolbar tbody tr td.ms-toolbar[width]:last").remove();
 
            // now build a link
            var $select = "<a href='" + wikiHome + "' class='ms-toolbar'>Wiki Home<a>";
 
            // add a separator pipe, append the link and then the padding
            $parent.append($tdSeparator).append("<td class='ms-toolbar' nowrap>" +
            $select + "</td>").append($tdLastCell);
        }
    }
 
    jQuery.event.add(document, "ready", addWikiHomeButton);
</script>

.

Labels: , ,

Need support from experts?

Access our Forum

Download Free Trials