Welcome to Atalasoft Community Sign in | Help

Manipulate files in the GAC with C#

Ever wonder how SharePoint takes those files you’ve carefully wrapped up in a solution package and installs them to web front ends?  Specifically, DLLs that need to go into the GAC?  Here’s how.

First of all, if you have Visual Studio 2003, in its SDK directory is a gem of code that is undocumented by Microsoft (for the most part), the FusionInstall.cs file.  This file (along with IAssemblyCache), by code, explains how to programmatically gain access to the GAC (or either of the other two assembly caches) for file manipulation.  This might be old hat to some of you out there, however, I’d like to point out that this class (and supporting interface) is copied verbatim into SharePoint’s own DLLs.

To see what I mean, fire up Red Gate’s awesome .NET Reflector and open the Microsoft.SharePoint.dll file.  Once opened, browse into the Microsoft.SharePoint.Administration namespace and expand the FusionInstall class as seen in the screenshot below

screenshot

 

Notice, the Analyzer window is showing you that the AddAssemblyToCache() function is used by Microsoft.SharePoint.Administration.SPSolutionPackage.UpdateGacFile() function!  Excellent, so now you know how SharePoint gets files into the GAC.  The other piece to the puzzle is gaining rights to allow files to be put into the GAC at all!  You can see how SharePoint accomplishes that by right-clicking the UpdateGacFile() function in the Analyzer window and selecting “Go To Member” from the context menu.  In the UpdateGacFile() function an object of type SPAdministrationOperation is built to use if the current user (that is, the user of the timer service) isn’t admin, then is dispatched to do the actual GAC installation.  I’ll eave it to you to figure out what the SPAdministrationOperation does in detail.

Of note, however, is the DoGacInstall() method call, which is where SharePoint actually uses the FusionInstall static class. Take a closer look at that class and you’ll find a nice way to deploy and retract assemblies from the gac!

Posted by dterrell | 0 Comments

VizitSP uses SharePoint Solution Installer, so does Microsoft

While looking into SharePoint Search Server 2008 Express and ways we can customize the search results for our customers a coworker suggested I look at Faceted Search, an Open Sourced project originally an add-on for SharePoint that’s written by Microsoft.  It was mentioned on CNet last year, hosted by CodePlex and uses the SharePoint Solution Installer to aid installation of the solution file into your SharePoint farm. 

It’s interesting to note that they chose to use the SharePoint Solution installer, as we did, to make solution installation easier on our customers.  Ours is only slightly modified to include the solution file in the EXE and to not use an external configuration file, but it’s a long cry from using stsadm for solution installation which is what Microsoft leaves you with by default!  stsadm is great in its own right, the extensibility is great, and there are a ton of add-ons.

In Matt Asay’s article about Microsoft open sourcing Faceted Search, he says, “It's not clear how anyone could use this open-source code beyond Sharepoint, thereby limiting its utility, but I still think it's a step in the right direction for Microsoft.”  I think it’s clear how people could use this!  For starters, Matt mentions tweaking, which I agree with, but also my biggest use it to learn from it.  Microsoft is the expert in SharePoint and SharePoint Search, after all, so I know I’ll be dissecting and learning from the source code for a little while.  I view it as a complete example of how to build webparts for SharePoint as a sort of extension to all the MSDN documentation you can read.  How can you use it beyond SharePoint?  Directly, there is no way.  Indirectly, I think Microsoft is trying to show how search should be.  I like sites like Newegg for their search.  The ability to drill down on products is a good thing, but I think Microsoft is trying to show that all of that searching, all of the drilling, can be cut shorter by showing you all of the facets of your search in one shot.  It can be information overload, but customized properly it should allow you to get at the information you want faster.

Posted by dterrell | 1 Comments
Filed under: ,

New Toy: SharePoint Search Server Express 2008 – Let’s break it!

In this article we’ll take a shallow look at how to customize the search results by finding the Search Core Results web part editing its output XSLT a bit.  Yes, Microsoft Search Server 2008 Express has been out for quite some time, but I’ve just now started playing with it in order to work on some VizitSP items I have for our upcoming releases. 

There’s a lot of information out on the web covering these topics; I’m certainly not the first, but I like having the reference for personal use later if I ever have to return to the topic.


Search Core Results – How to edit the search output

A little poking around with web parts and you’ll know right where to go.  Begin by running a simple search:

search

We’ve all seen this friendly, innocuous search box, so use it!  I have one item in two crawled lists on my SharePoint installation, so I search for it by name and get the standard result:

 res

Click on the Site Actions button in the upper-right of the search results page and select Edit Page:

edit

You’re then presented with the page editor that will show you all of the web parts that exist on the search results page:

webpart

Now, in order to edit the results click the down arrow on the upper-right corner of the Search Core Results webpart.  This will add another frame to the view on the right edge of the page with Search Core Results Options.  Click the Modify Shared Web Part link to get to the guts of how this webpart is displayed:

xslt

Now the fun begins.  To start off, click in the XSL Editor window, hit ctrl-a and ctrl-c to copy everything in there, then paste it into a window that’s reasonable to work with.  In some browsers, that editor window is resizable, but it’s still ridiculous to work in.

MSDN has a great article that explains all the workings of the search results XSLT.  Generally speaking, though, you’ll probably concentrate on the area at or around line 130 that is the template responsible for showing the results: <xsl:template match="Result">

To see what XML you’re transforming with this XSLT, check out this article, also on MSDN on how to display the result XML.

A quick an easy way to see the results of an edit would be to go to the c0 template, which matches on the first search term of a search.  Normally this template simply bolds the search term in the results:

<xsl:template match="c0">
  <b>
    <xsl:value-of select="."/>
  </b>
</xsl:template>

but by simply replacing the bold tags with a span tag and some style, you can get highlighting:

<xsl:template match="c0">
  <span style="background-color:yellow">
    <xsl:value-of select="."/>
  </span>
</xsl:template>

And the results:

highlighted 


We’ve just splashed the surface into search, but there’s obviously a lot that wasn’t looked at, like adding custom webparts to this page.  Codeplex has a project, Faceted Search, that is a good example of how far you can go with it.

 

Until next time!

Posted by dterrell | 0 Comments
Filed under: ,

Overriding SharePoint WebControls: DateTimeField

Abstract

Rendering Microsoft’s SharePoint WebControls can be a great way to write customized field editor.  However, when their controls fall short or malfunction how can you avoid writing your own to do the job?  This article shows some powerful tools you can use to find the issue and hopefully fix it with minimal code writing.

 

Required Tools

  • .NET Reflector - If you don’t already have this one in your developer toolbox, then get it.
  • FireBug – An excellent plug-in for Firefox web browser, this tool will help you isolate elements, step through js, see network traffic, and more!
  • User Agent Switcher – Another Firefox plug-in, when something won’t go in Firefox (often for SharePoint controls) you need to fool it
  • A sense of humor – Lots of times you find exactly what you want and there’s nothing you can do about it (private, protected, internal, oh my!)

 

Introduction

I’m working on the Property Editor as part of our product VizitSP viewer.  The property editor is a panel that shows the metadata about a document.  It displays all the fields for that document’s content type, and allows you to edit them.  We do this through some simple code that gets the document, gets its content type, and iterates through that content type’s fields and adds the field’s RenderingControl to the page.  An issue comes up in a few spots, one which I’ll tell you about in this article.  Really, the information I want to convey in this article is how much knowledge you can gain with a little poking around.

 

The Problem

Microsoft’s DateTimeField already takes care of a lot of heavy lifting.  Our problem arose with the frame when it is rendered and its onload event tries to do some positioning.  It looks like the calendar doesn’t like being so close to the right edge of the screen and moves over when it is.  Here’s how it looks in our Property Editor:DTF

Here you can see the dateTime field, its Calendar icon and the rendered calendar that’s been pushed to the left.  Ack!  It’s under the scroll bar for the neighboring frame.  Hmm…

 

DTF2 

This is what the dateTime field’s DatePicker should look like.

 

Let’s start by figuring out how this tool is rendered.  Fire up Firefox and go to your page with the DateTime field and…AWE!  It’s not there?  The DateTime field is an example of a control that Microsoft chooses not to render on ‘non type-1 browsers.’  What does that mean?  No IE?  No workie!  Let’s fool ‘em with the user agent switcher and get that DateTime icon to come up…Once you do, open up Firebug and let’s figure out what this thing is doing.

fb Click the Inspect button on Firebug’s bar, and mouse-over the calendar until it has a blue border, then left-click the calendar to lock the inspector on that element.  The calendar icon is inside an anchor tag <a> whose onclick event calls clickDatePicker() function.  We can find the clickDatePicker() function by clicking Firebug’s Script tab, and then just above that there’s a button with the name of the page you’re on.  Clicking that will bring up a menu of all the javascript-containing elements (including pages with embedded js).  Going through the list one will definitely stick out: datepicker.js.  Click it and the Firebug window will now contain the javascript complete with line numbers (that turn green when they’re hit) where you can put break points, a Watch/Stack/Breakpoints window to the right, this is all very familiar stuff!

Scroll through the javascript until you find the clickDatePicker() function.  A little, light reading and you come to discover the DatePicker is an iframe, and at around line 346, its source attribute is changed to iframesrc + datestr.  iframesrc is the 5th argument, moving again up the stack to clickDatePicker() that variable is src, which comes from its second argument.  Moving further up the stack is the calendar icon’s anchor’s onclick event that passes as the second argument “/_layouts/iframe.aspx? …”.  We have all the pieces to this puzzle, first is datepicker.js (which we know) and second is iframe.aspx.

iframe.aspx is a a simple aspx page that includes the datepicker.js and whose onload is

   <BODY onload="PositionFrame('DatePickerDiv');" onkeydown="OnKeyDown(this);" style="margin:0;">

Find PositionFrame() in datpicker.js and you’ll see, around line 190:

   1: var cxBody=document.parentWindow.parent.document.documentElement.offsetWidth;
   2: if (ifrm.style.pixelRight+elt.offsetWidth > cxBody)
   3: {
   4:     ifrm.style.pixelRight=ifrm.style.pixelLeft;
   5:     ifrm.style.pixelLeft -=elt.offsetWidth;
   6: }

Which is a bit strange; this code runs if the calendar’s right edge plus the calendar’s width are off the right edge of its parent’s right edge.  If you’re window is sized down enough so that its right edge is within one calendar-width of the calendar’s right edge, this will move the calendar over one calendar-width to the left!  Go figure.  In RTL (Right-to-Left) layout, this might make sense, but in our Property Editor, that code causes the issue shown above.

If this is just your SharePoint installation, then you can just go ahead and comment lines 190~195 of datepicker.js and refresh your browser (after a cache clean) and you should be good to go.  However, we deploy VizitSP to customers and can’t just go overwriting files in their SharePoint installation.  Not to mention it’s unsupported!

Well, let’s go see how the DateTimeControl is rendered to get a sense of how this is strung together.  We already know the iframe.aspx and datepicker.js files play a role, we know it’s DateTimeField, so open up the Microsoft.SharePoint.dll in Reflector and go into the Microsoft.SharePoint.WebControls.DateTimeField.  If you just start poking around, you’ll find what you’re looking for, and probably other things, too!  Let’s just cut to the chase…

In the instance fields you’ll find m_urlDatePickerFrame and m_urlDatePickerJavaScript, curious.  Right-click on one those fields to select Analyze

 

murldatepickeralt

 

The Analyzer window will open (if it isn’t already) and will be populated with a new entry: Microsoft.SharePoint.WebControls.DateTimeControl.m_urlDatePickerJavaScript : String

Expand that, and expand Used By to tell you what code uses this field.  There are 4, the ctor (constructor), the getter and setter (these will be interesting later), and, ahh: OnPreRender().  Right-click the OnPreRender() method in the analyzer window, select Go To Member and start reading.  Eventually, you’ll hit this (ignore the line breaks):

this.Page.ClientScript.RegisterClientScriptBlock(typeof(DateTimeControl), "datepicker_js", "<SCRIPT language='javascript' src='"
SPHttpUtility.HtmlUrlAttributeEncode(this.m_urlDatePickerJavaScript)
"'></SCRIPT>");
 
Ahha!  We might be able to override the js and iframe!  The question is how to set this field so we can control it.  If you Go back to the Analyzer window and go to the set_DatePickerJavaScriptUrl(String) method you see it’s public, and it does nothing special.  Expanding that method in the analyzer and expand the Used By…nothin’.  Bummer!  Hey, that doesn’t matter that much, it’s public and we can set it before the control is rendered.  The next biggest hurdle to get over is the fact that DateTimeField has its DateTimeControl as private.  I want to mess with it as little as possible, so a little poking around the m_dateTimeControl field in DateTimeField and you’ll end up in CreateChildControls().  There you’ll find:
 
this.m_dateTimeControl = (DateTimeControl) this.TemplateContainer.FindControl("DateTimeField");

Well, heck, we can do that!  Let’s get to it.

 

The Solution

Open up your solution and add a new class.  In the class begin by extending the DateTimeField:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using Microsoft.SharePoint.WebControls;
   5: using Microsoft.SharePoint;
   6: using System.Security.Permissions;
   7:  
   8: namespace Vizit.SharePoint
   9: {
  10:     public class VSPDateTimeField : DateTimeField
  11:     {
  12:         private DateTimeControl m_dateTimeControl;
  13:  
  14:         protected override void CreateChildControls()
  15:         {
  16:             if (base.IsFieldValueCached)
  17:             {
  18:                 base.CreateChildControls();
  19:                 return;
  20:             }
  21:  
  22:             SPFieldDateTime field = (SPFieldDateTime)base.Field;
  23:             if (field == null) return;
  24:             base.CreateChildControls();
  25:             if (base.ControlMode == SPControlMode.Display) return;

Boilerplate, we’ve inherited DateTimeField and followed its conventions with our own m_dateTimeControl, and overriding the CreateChildControls() method.  Next, we get a reference to the DateTimeControl:

            this.m_dateTimeControl = (DateTimeControl)this.TemplateContainer.FindControl("DateTimeField");
            if (this.m_dateTimeControl == null)
                throw new ArgumentException(SPResource.GetString("InvalidControlTemplate", new object[] { "DateTimeField" }));

Up to now, we’ve just been reiterating the base type’s code.  But now we get to override the JavaScript path, and iframe path:

if (base.ItemContext.Web != null)
{
    this.m_dateTimeControl.DatePickerFrameUrl = base.ItemContext.Web.ServerRelativeUrl + "/_layouts/myDatePicker.aspx";
    this.m_dateTimeControl.DatePickerJavaScriptUrl = base.ItemContext.Web.ServerRelativeUrl + "/_layouts/myDatePicker.js";
}
 
The last step to this is making the iframe use your js in its onload event, simple.  In the aspx page, change the script line to point to your js instead:
 
<script src="./myDatePicker.js"></script>

Sweet!  Now you can copy those to files to myDatePicker.aspx from iframe.aspx and myDatePicker.js from datepicker.js in the Layouts folder and make any changes you want.  When you need to render a DateTimeField you can render your control instead where your aspx and js will be used, giving you a good amount of freedom with Microsoft’s code. 

 
 

Conclusion

This is just one example of how you can reflect through SharePoint dlls, read through aspx pages, and javascript code to arrive at a very quick answer to what could be a nightmare of a problem (who wants to rewrite an entire date picker when there’s already a good one available, DRY anyone?).  Taking what you know here, you could extend this to render the DateTimeField in Firefox or other non type-1 browsers, but it’s a lot more work.  Hopefully I’ve given a good outline of the massive power .NET Reflector lends to a SharePoint developer, along with the tools of any web developer, Firebug and the User Agent Switcher.

Posted by dterrell | 0 Comments

Mocking SPSecurity delegates

Since we’ve recently released VizitSP it’s time to start talking about more things SharePoint.  Quickly, I’m going to show you how we’ve used TypeMock’s Isolator for SharePoint along with NUnit and TestDriven.NET to test our use of SPSecurity’s static method RunWithElevatedPrivileges().  The main problem I found was that SPSecurity had to be mocked, otherwise it would throw a NullReferenceException.  Here’s an example:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4:  
   5: namespace Test
   6: {
   7:     [TestFixture]
   8:     public class Test
   9:     {
  10:         [Test]
  11:         [Isolated]
  12:         public void TestUsingSPSecurity()
  13:         {
  14:             SPSecurity.RunWithElevatedPrivileges(delegate()
  15:             {
  16:                 //
  17:             });
  18:         }
  19:     }
  20: }

In this example it would be thrown at line 14 for obvious reasons, there’s no SharePoint to test against!  Let’s flex those Mock muscles and handle calls to SharePoint’s static objects.

First, we need need to tell TypeMock that we’re going to Mock the SPSecurity object

using TypeMock;
...
public void TestUsing SPSecurity()
{
...
Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
...

Now, on the mocked SPSecurity object, we want to redirect calls to its RunWithElevatedPrivileges() function.  Start typing spSecMock.MockMethodCalled += and when you get to the “=” Visual Studio’s awesome code completion will step in:

image

Hit <tab> twice and you’ve got not only the whole method handler invocation but it’ll insert a stub handler for you.

Now we have to tell TypeMock to expect calls from RunWithElevatedPrivileges

spSecMock.ExpectAlways("RunWithElevatedPrivileges");

And we want to, in our method handler, only handle the RunWithElevatedPrivileges method calls, which we can get from the event args:

void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
{
    if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
        throw new System.NotImplementedException();
}

Just to show that it’s working, and we can actually mock SPSecurity, we could stop here and have NUnit expect the NotImplementedException:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4: using TypeMock;
   5:  
   6: namespace Test
   7: {
   8:     [TestFixture]
   9:     public class Test
  10:     {
  11:         [Test]
  12:         [Isolated]
  13:         [ExpectedException(typeof(System.NotImplementedException))]
  14:         public void TestUsingSPSecurity()
  15:         {
  16:             Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
  17:             spSecMock.MockMethodCalled += new MockMethodCalledEventHandler(spSecMock_MockMethodCalled);
  18:             spSecMock.ExpectAlways("RunWithElevatedPrivileges");
  19:  
  20:             SPSecurity.RunWithElevatedPrivileges(delegate()
  21:                 {
  22:                     //
  23:                 });
  24:         }
  25:  
  26:         void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
  27:         {
  28:             if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
  29:                 throw new System.NotImplementedException();
  30:         }
  31:     }
  32: }

Now we want the method handler to run the delegate code passed to RunWithElevatedPrivileges!  To do that we retrieve the delegate code from the event arguments and invoke it!  The whole method handler should look like this:

void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
{
    if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
    {
        SPSecurity.CodeToRunElevated del = (SPSecurity.CodeToRunElevated)e.SentArguments[0];
        IAsyncResult res = del.BeginInvoke(null, null);
        res.AsyncWaitHandle.WaitOne();
        del.EndInvoke(res);
    }
}
 
So, if we hand the method something to run, we can see it’s working.  Modify the interior of your anonymous delegate code, throwing the NotImplementedException there:
 
SPSecurity.RunWithElevatedPrivileges(delegate()
{
    throw new System.NotImplementedException();
});

Run this test, and it’ll still pass, meaning you’re properly calling into the delegate you passed to RunWithElevatedPrivileges()!

Finally, when you’re doing something elevated (and you’re not, are you?) TypeMock needs to step in there, too.  The easiest thing to do is ensure the next instance of some object is faked by TypeMock.  We want to work with some site we create inside the elevated code, and we need to mock that, too; here’s how:

   1: Guid siteGuid = Guid.NewGuid();
   2: SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
   3: Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
   4: Isolate.WhenCalled(() => fakeSite.ID).WillReturn(siteGuid);

You create a fake SPSite object using TypeMock’s Isolate.Fake.Instance() method.  Then, as you see on line 3, ensure that our mocked object is returned the next time an SPSite is constructed.  On line 4, we instruct TypeMock to return our local GUID, so we have some basis of comparison later on.  When you go to create a new site in your elevated code you can see that the returned ID is that of the fake constructed site:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    SPSite elevatedSite = new SPSite(siteGuid);
    Assert.AreEqual(elevatedSite.ID, fakeSite.ID);
});

 

So you can see the whole thing together:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4: using TypeMock;
   5: using System;
   6:  
   7: namespace Test
   8: {
   9:     [TestFixture]
  10:     public class Test
  11:     {
  12:         [Test]
  13:         [Isolated]
  14:         public void TestUsingSPSecurity()
  15:         {
  16:             Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
  17:             spSecMock.MockMethodCalled += new MockMethodCalledEventHandler(spSecMock_MockMethodCalled);
  18:             spSecMock.ExpectAlways("RunWithElevatedPrivileges");
  19:  
  20:             Guid siteGuid = Guid.NewGuid();
  21:             SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
  22:             Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
  23:             Isolate.WhenCalled(() => fakeSite.ID).WillReturn(siteGuid);
  24:  
  25:             SPSecurity.RunWithElevatedPrivileges(delegate()
  26:             {
  27:                 SPSite elevatedSite = new SPSite(siteGuid);
  28:                 Assert.AreEqual(elevatedSite.ID, fakeSite.ID);
  29:             });
  30:         }
  31:  
  32:         void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
  33:         {
  34:             if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
  35:             {
  36:                 SPSecurity.CodeToRunElevated del = (SPSecurity.CodeToRunElevated)e.SentArguments[0];
  37:                 IAsyncResult res = del.BeginInvoke(null, null);
  38:                 res.AsyncWaitHandle.WaitOne();
  39:                 del.EndInvoke(res);
  40:             }
  41:         }
  42:     }
  43: }

 

Keep in mind, this is an example of how to get into delegates with TypeMock, so it could be extended into Impersonation and The Right Way™ of working with SharePoint security.

 

Happy Mocking!

Watch out where you use SPWebConfigModifications

Just a simple post: if you’re developing a feature for SharePoint, watch out if you’re uinsg SPWebConfigModifications.  An example issue might be if you put it in your feature receiver, and as a farm admin try to activate that feature when you don’t have rights to modify the actual web.config.  An access denied exception will be thrown and your mods won’t get put in.  Just keep it in mind.

Posted by dterrell | 0 Comments

CustomActionGroup: Making your own grouping in SharePoint admin pages

CustomActionGroups are a way to group together CustomActions in your SharePoint admin pages.  Go figure!  I’m going to give you a quick example of how these things work.

A CustomActionGroup looks like this:

image

InfoPath Forms Services is the group, under which a number of CustomActions have been grouped.  To do this you need to know how to create a Feature.  For this example I’m going to use this feature.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Feature Id="7E710DE2-D8DB-444D-A96F-9513787CEA0B"
    Title="UI Custom Actions"
    Description="This example shows how you can customize various areas inside Windows SharePoint Services."
    Version="1.0.0.0"
    Scope="Site"
    xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="UICustomActions.xml" />
  </ElementManifests>
</Feature>

and since you’re already a feature pro, you want to know what the ElementManifest file looks like because, after all, that’s where the content of this blog post is.  Without delay, here it is:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomActionGroup Id="CA_Company"
                     Location="Microsoft.SharePoint.Administration.ApplicationManagement"
                     Title="Company Product Group" />
  <CustomAction Id="CA_CompanyThing"
                Title="Manage your Company Product installation"
                GroupId="CA_Company"
                Location="Microsoft.SharePoint.Administration.ApplicationManagement"
                Sequence="0"
                RequireSiteAdministrator="TRUE">
    <UrlAction Url="_layouts/Company/Action.aspx"/>
  </CustomAction>
</Elements>

You’ll notice a few things, first the Location of both items is set to Microsoft.SharePoint.Administration.ApplicationManagement.  This example will make a new group in the Application Management section of the Central Admin page in SharePoint.  You can check out the Default Custom Action Locations and IDs page on MSDN to figure out where else you can put your custom action groups and custom actions.

Next, have a look at the CustomAction’s GroupId attribute.  The GroupId is normally where you’d put something like SiteAdministration if you were to put your CustomAction in the Microsoft.SharePoint.SiteSettings Location.  In this case I’m using the CA_Company GroupId, which makes sense since we want the CustomAction to show up in our newly created CustomActionGroup.  Notice, there are a handful of optional Attributes on the CustomActionGroup that I haven’t used here, but you can have a look at the schema to see them all.

When you install and activate this feature

stsadm -o installfeature -filename company\feature.xml
stsadm -o activatefeature =filename company\feature.xml -url http://yourserver:32342

you should see your new group and action listed in the Application Management screen

image

Enjoy your grouping!

Posted by dterrell | 0 Comments

Shift_Job

Just a quick note that my responsibilities in Atalasoft shift again from Build Czar –> DotImage Engineer –> Vizit Engineer –> Installer Czar –> Vizit Engineer.  So I am back on Vizit engineering again, and once again banging my head on SharePoint.  This past weekend I attended SharePoint Saturday, VA Beach along with my co-worker Jacob with whom I’ve worked very closely in every aspect of my job (since I took over Build Czar role from him when I started and followed him into Vizit Engineering).  Most of the trip was discussing with Jacob various changes that had been made to the Vizit code base since I was working with him previously. 

The SharePoint camp was 1 part of the trip that encompassed 3 days (Friday to Sunday, with SharePoint Saturday on…well, Saturday!).  The camp was already a good size.  I haven’t been to any other code camps yet, but judging this one, it was already clear they had done a good job organizing key speakers with good experience.  Jacob presented a lot of material that I worked on with him, and he has a few blog posts that cover it as well.

Since I’ve been working on SharePoint, while keeping most of that work under the radar, I wanted to take a minute to cover just two of the blogs I’ve been to.

First, since I’ve been working a bit on our administration page check out Ton Stegeman’s blog and specifically his post on Creating a site administration page that looks like a real SharePoint administration page which I followed, more-or-less, to get our Vizit admin stuff working.

I also learned a great deal from Chris O'Brien's blog and his excellent apps using the SharePoint object model, in particular his SharePoint Config Store that is a great example for everything from Solution to SPListItem and how to use them.  Also, his post on Custom Actions to modify system pages along with Ton’s post about admin pages are a great pair to get going in that area.  For those of you who are interested in in this area of SharePoint development check out Microsoft’s documentation on the Default Custom Action Location and IDs to help map it out.  Using this information, in my next post, I’ll do a quick once-over of adding your own group and Custom Actions, it’s pretty easy!

Posted by dterrell | 0 Comments

Extending WiX Preprocessing: A few debug examples

This time we’ll talk about debugging that shiny new preprocessor code.  Since Visual Studio will lock your DLL when you use it to build something, the development cycle gets really slowed down by closing Visual Studio, editing/rebuilding your code, then restarting Visual Studio to test.  I’ve found a few ways to do this that are probably pretty obvious, but I’d figure I’ll share them anyway.


First, and probably the most obvious and I think the best way, would be to use you class library in a test assembly.  You make some test fixtures, in my case and XmlDocument, and pass it in to the required function for processing.  Very simple, it works, but you don’t get to see everything in action within Visual Studio.  If you really want to have the document look the way it’s going to look when Candle calls into your preprocessor, run the preprocessor once and have it spit out its result by specifying –oSomeFile.xml on the command line, or in the project properties under Tool Settings->Additional parameters->Compiler (don’t forget to remove this flag once you’re done, with that flag on the preprocessor sends no output to the compiler)!

Second, use Visual Studio’s JIT debugging.  This doesn’t solve the problem of Visual Studio locking your assembly, but what’s nice is you get at your code inside Visual Studio, in the build process as it would be compiling with VS.  The way to do it is to tell the CLR that you need debugging

Debugger.Launch();
 
You’ll need the System.Diagnostics namespace for that one.
 
Last, but not least: use the command line!  The easiest way I’ve found to getting all the options Candle uses is to compile it once with Visual Studio, go to the output window and copy the command line for candle.exe.  You can either pull the entire path to Candle.exe or, remember that WiX sets up an environment variable that points to its install directory, so replace the path with “%WIX%\bin\candle.exe” and you’re ready to go!  Compile your preproc extension in Visual Studio, then run Candle from the command line.  You’ll need to throw a Debugger.Launch() call in there, but the debug process is a lot less painful.
Posted by dterrell | 0 Comments

Extending WiX Preprocessing: Getting at the WiX source as a preprocessor (with Reflection)

In my last blog post, I showed how easy it is to extend WiX, and make a simple preprocessor extension.  It barely went beyond the existing WiX preprocessor example, but it get’s you off the ground.  This time, I’d like to look at an available override you can do to get at the WiX source for editing as part of the preprocessor.

Visual Studio’s intellisense (or a quick look at the source code) will show you  there are a few other overrides you can use in the preprocessor, one of which is the PreprocessDocument().

PreprocessDocument works differently than the previous example, in that you’re not doing simple functions to do variable replacement.  Rather, the signature tells you you’re getting the WiX source as an XmlDocument type.  This is excellent news, since it allows you great power over what XML is sent to the compiler.  I’m going to show you how we have ours setup to make it really easy to extend.

First, read my last blog post and Peter Marcu’s blog post on the subject to get setup with a WiX preprocessor extension.  If you’re just following along here, you don’t need to override EvaluateFunction().

Next, in your PreprocessorExtension class, add an override to PreprocessDocument:

public override void PreprocessDocument(XmlDocument document)

I want adding WiX preprocessing to be easy, so again I’m using reflection and a predefined search word.  I decided to make all Atalasoft nodes, by name, a search item.  I then use the located node’s attribute to decide which function to call.  Here’s an example of a node you put in your WiX:

<Atalasoft Example="$(var.Something),somethingElse" />

We’ll set it up so that this Atalasoft node causes the Example() function to be called with its attribute’s value as the argument.

Inside your Preprocessdocument, get it setup to read the XML using the proper namespace

private XmlNamespaceManager _man;
_man = new XmlNamespaceManager(document.NameTable);
_man.AddNamespace(xmlPrefix, http://schemas.microsoft.com/wix/2006/wi);
(_man is actually a private class-member variable).

Then, there’s the heavy lifting part

   1: XmlNodeList returnNodes = LocateAtalasoftNodes(document, _man);
   2:  
   3: while (returnNodes.Count != 0)
   4: {
   5:     XmlNode atalaNode = returnNodes[0];
   6:     foreach (XmlAttribute at in atalaNode.Attributes)
   7:     {
   8:         MethodInfo m = this.GetType().GetMethod(at.Name);
   9:  
  10:         try
  11:         {
  12:             m.Invoke(this, new object[] { document, atalaNode, _man, at.Value });
  13:         }
  14:         catch (Exception e)
  15:         {
  16:             Exception n = new Exception(string.Format("When calling {0} got exception: {1}.  Called as {2}({3}, {4}, {5}, \"{6}\")",
  17:                 m.Name, (e.InnerException == null) ? e.Message : e.InnerException.Message, m.Name, document.GetType(), atalaNode.GetType(), _man.GetType(), at.Value), e);
  18:             throw n;
  19:         }
  20:     }
  21:  
  22:     returnNodes = LocateAtalasoftNodes(document, _man);
  23: }
 
Let’s dissect this a bit:
 
You’re first instinct might be to replace the LocateAtalasoftNodes() and the while() loop with a foreach() iterator; watch out!  When editing XML documents, adding and removing elements causes previous iterators to be invalid.  Notice on line 22 we refresh the collection of found nodes after each function has been called to be certain we have a valid collection.  This also places an inherent requirement on the functions: that they actually remove at least the Atalasoft node they’ve been passed from the containing XmlDocument.  We probably could put this in some parent class to enforce that behavior, but I have yet to do it so I won’t pretend I have.
 
Line 1’s call to LocateAtalasoftNodes() is actually a simple FindNodeByName() wrapper so that I might later be able to add to it more functionality:
XmlNodeList returnNodes = FindNodeByName(doc, AtalaPreProc.NodesByName, man);
 
I setup NodesByName as a static getter, so it feels like a prefix in the EvaluateFunction example.  This would also later facilitate further reflection into this extension.  Setting this up like the previous EvaluateFunction example one could find all types of some base type and retrieve nodes of the ‘NodesByName’ name.  At the moment I have it so we just look for Atalasoft nodes:
public static string NodesByName { get { return "Atalasoft"; } }

On line 6 we iterate through all of the node’s attributes and evaluate the attribute’s name as a function name.  Just as easily, and perhaps better style, would be to get the first (and what should be the only) attribute and use that.  Notice on line 8 I use the attribute’s name as a parameter to the GetMethod() reflection method.  We probably want some error checking around this to be sure it doesn’t return null!

Then, on line 12, I invoke the method and hand it a few parameters.  Needed for the function to do its work, the first parameter is the parent XmlDocument where the node was found, the second is the located XmlNode that caused this function to be called (later to be removed by the function), the XmlNamespaceManager, and the value of the node’s attribute (not really needed since the function can retrieve that itself).

If you apply this to the above example of an Atalasoft node, you’ll see that this code will call the Example() function with a single string as an argument.  That string will contain the preprocessor replacement of

$(var.Something) 

and the text “,somethingElse”.  “Ahha!” You think, “he’s going to do some split to obtain multiple function arguments.”  We actually do that in the function, so it’s not up to the parent reflection code.  You could go a step further and split the string on commas, or something, and try to parse through the function argument types, then find the function with the best-matching signature.  I decided to keep this a bit more simple and let the function itself worry about how it should split things up.

From here, what Example() does doesn’t matter.  Suffice it to say it can do anything with the document it wants, but at very least in my setup it must do this:

public static void Example(XmlDocument doc, XmlNode node, XmlNamespaceManager man, string arguments)
{
    // Replace me with generated XML
    XmlNode parent = node.ParentNode;
    parent.RemoveChild(node);
    parent.InnerXml += filesDirs.ToString();
}

After that, you can see I wrap any exceptions that get thrown in order to help my logging output be more clear and aid debugging on the WiX side.  And, finally, on line 22 you see that I refresh the returnNodes collection, since it should have been invalidated by the aforementioned function call.

Now we have a problem: you have some C# code and only one way to test it (albeit a poor one).  From here, you build this into an assembly and reference the compiled DLL in your WiX project.  If anything goes wrong you get to shut down Visual Studio since your preprocessor extension is used by it, edit your code, recompile, restart Visual Studio, then try to compile your WiX project.  Rinse and repeat ad nauseam.

In my next post, I’ll show you some easy ways to help the development cycle of your WiX Extension.

Posted by dterrell | 0 Comments
Filed under: , , ,

Extending WiX Preprocessing: A ‘How-To’ with Reflection

The authors of WiX have made it easy for you to do some of your own preprocessing on your WiX files before they get handed to the compiler (hence PRE of ‘preprocessing’).  Aside from the regular preprocessor functions you can use (variable replacement, conditional preprocessing, iteration) which makes WiX feel more like your regular programming language, they’ve taken it one step further and allowed for extension of the preprocessor, and to do it is very easy.

To do it, head on over to Peter Marcu’s Blog post on WiX: Functional Preprocessing from last year to get an idea of what I’m talking about.  Now, if you look at the WiX source, there is an example preprocessor extension already there.  I’ve boiled one layer out of it to get mine, but the example is great to get you off the ground; it’s located in the WiX solution under the Src folder, the project is called “PreProcExampleExtension.”

First, you get setup by creating a new project (assembly) and make your new class extend the WixExtension type and override the PreprocessorExtension:get method.  Notice, we call ours AtalaPreProcExt which is a poor name considering at that level it’s a WixExtension, but returns the PreprocExtension when overriding its get property.

   1: namespace Microsoft.Tools.WindowsInstallerXml.Extensions
   2: {
   3:     public sealed class AtalaPreProcExt : WixExtension
   4:     {
   5:         private AtalaPreProc ext;
   6:  
   7:         public override PreprocessorExtension PreprocessorExtension
   8:         {
   9:             get
  10:             {
  11:                 if (null == ext)
  12:                     ext = new AtalaPreProc();
  13:  
  14:                 return ext;
  15:             }
  16:         }
  17:     }
   18: }

So far, so good, and so easy; just following the example code provided.  I chose to compact it a bit more, and put the AtalaPreProc class right in the same file.

The next thing you need to do is provide to the preprocessor an array of prefixes you handle.  You know how the WiX Preprocessor does ‘var.*’ variable replacement, well the ‘var’ is the prefix you need to tell it.  Here is where the magic happens.  I wanted to be able to quickly and easily extend this further, allowing for different prefixes easily to be added without having to worry about some hardcoded list.  For this I used an abstract class and a bit of reflection to return the list:

   1: public override string[] Prefixes
   2:         {
   3:             get
   4:             {
   5:                 if (_handles == null)
   6:                 {
   7:                     // This should be a list of the prefixes we handle, this directly corresponds to the classes in this assembly
   8:                     List<string> classes = new List<string>();
   9:                     foreach (Type t in this.GetType().Assembly.GetTypes())
  10:                     {
  11:                         if (t.IsSubclassOf(typeof(AtalaPP)))
  12:                             classes.Add(t.Name);
  13:                     }
  14:  
  15:                     _handles = classes.ToArray();
  16:                 }
  17:  
  18:                 return _handles;
  19:             }
  20:         }

Here, _handles is a private string[] in the class.  If you know reflection, then you know what this does: it’ll find all classes in this assembly that extend AtalaPP, and takes the type name as its prefix.  AtalaPP is a stub class (at least for us) for now.  All you’ll have to do from here is add a new class and extend the AtalaPP type:

internal class Atala : AtalaPP

And from there, any public, static function that takes a single string as a parameter becomes a preprocessor function!  In this case, Atala is our prefix and, for example, Example is the function:

   1: internal class Atala : AtalaPP
   2: {
   3:     public static string Example(string input)
   4:     {
   5:         return "This is the Atala.Example() preproc function, you gave me: " + input;
   6:     }
   7: }

You use this in WiX like this:

<?define Example          = "$(Atala.Example(someString))" ?>

Then whenever $(var.Example) is used, it’s replaced with “This is the Atala.Example() preproc function, you gave me: someString.”

Next time I’ll show you another excellent preprocessor function you can override that will allow you to manipulate the WiX code itself, as opposed to simple variable replacement.

Posted by dterrell | 1 Comments
Filed under: , ,

WiX Preprocessing

I know this is old, but Peter Marcu has a great 2-parter on how to make a preprocessor extension for WiX:

 

There's also an example of this in the WiX source...sure goes a long way!

Posted by dterrell | 0 Comments

Using SPWebConfigModification, Part 6 - Putting it to work

 

Putting it to work

 

So, using this code, you could embed in your Feature receiver a web.config file that looks like the aforementioned fragment.  Read in the XML to an XmlDocument object, and then pass that XmlDocument along with your SPWebApplication or SPWebService’s WebConfigModification collection to a recursive starter method, like this:

private void AddModifications(XmlDocument doc, Collection<SPWebConfigModification> collection)

{

    XmlNode docRoot = doc.DocumentElement.FirstChild.ParentNode;

    SPWebConfigModification mod = new SPWebConfigModification(docRoot.Name, doc.DocumentElement.Name);

    mod.Sequence = _seq = 0;

    mod.Owner = _modificationOwner;

 

    AddChildren(collection, docRoot, mod);

}

Finally, be sure to apply those changes to the content service, and update the SPWebApplication or the SPWebService into which you’ve put those modifications:

SPWebService.ContentService.ApplyWebConfigModifications();

currentWebApp.Update();

I hope this helps everyone in their SharePoint tasks!

 

Posted by dterrell | 2 Comments

Using SPWebConfigModification, Part 5 - The Remedy

 

The Remedy

 

Ok, so now the Name property is only an XPath expression if it’s a child node.  Then what do we do with sections that have attributes?  They must be child nodes, and so created as EnsureChildNode type.  I’ll modify the code a little bit so that the first if statement looks like this:

if (string.IsNullOrEmpty(name) && node.HasChildNodes) {…

And move the Name property update into the Else{} block.  Try that!  Nope.  When SharePoint tries to process the  node under the <location path="_some/path/here"> node it freaks out saying it can’t find it!  Ok, now what?  Well, if we consider everything a section up until you get to a node that has attributes, then switch to EnsureChildNode types from there on, will that work?

Again, we change the first if statement and remember the parent’s type:

SPWebConfigModification.SPWebConfigModificationType parentType = mod.Type;

mod = new SPWebConfigModification(node.Name, GetOuterPath(node.ParentNode));

if (string.IsNullOrEmpty(name)

    && parentType == SPWebConfigModification.SPWebConfigModificationType.EnsureSection

     && node.HasChildNodes) { …

 

Now we’re off and running.  Everything in our fragment web.config file will be an EnsureSection type until that node has no child nodes, it has attributes, or its parent type wasn’t EnsureSection (it’s a child of a child).  In this way, you can build a web.config file fragment and not worry that you’ll add a modification child node when that node’s full path doesn’t yet exist!

I'll show you how to put this to work in Part 6!

 

Posted by dterrell | 2 Comments
More Posts Next page »