Wednesday, August 6, 2008

Making a MatchEvaluator Delegate "take" more parameters

So I was coding away a few months ago and was planning on using good old Regex.Replace() to process the contents of some files. But I needed it to be a little more robust in how it decided what text would be substituted in for a match. A quick search turned up the MatchEvaluator option. I had never used it before but is sounded like it would fit the bill perfectly. All you do is give it a delegate method to call when your regular expression finds a match. When your delegate method is invoked you can do anything you want to figure out WHAT string you want to pass back as the Regex match.

That led to some test code that looked like this:
   1:  class DelegateTest
   2:      {
   3:          public DelegateTest() { }
   4:          
   5:          public void Go()
   6:          {
   7:              MatchEvaluator oEval = new MatchEvaluator(DoSomeWork);
   8:              string strResult = Regex.Replace("This Is Test Content", "Test", oEval, RegexOptions.IgnoreCase);
   9:          }
  10:   
  11:          private string DoSomeWork(Match m)
  12:          {
  13:              string strTransformedText = string.Empty;
  14:              strTransformedText = "ABC";
  15:              return strTransformedText;
  16:          }
  17:   }

It worked great and I figured that was easy, cool I do that. Then a few days later I came to the point where I was ready to actually write the logic that would go into the delegate method. That's when I hit a snag. The MatchEvaluator delegate has ONE very specific signature it must adhere to. It only takes ONE parameter, a Match object. I needed access to a collection of objects that had been constructed prior to the Regex call. I tried making the object collection global but got an error about it not being in the current context, damn. I feared I was screwed and would have to take a more manual parsing approach.

I decided to do a quick check for delegate definition options and found
this post talking about anonymous methods. "Hot damn" I though, that just may work. I defined the delegate as an anonymous method and CHA-CHING!, I could use variables that where in scope just like normal. I end up factoring out the creation of the MatchEvaluator and its anonymous delegate into its own method because it basicly looked like a giant inline function defined in the middle of all my code. Not easy on the eyes.

After refactoring it ended up looking something like this:

   1:  class DelegateTest
   2:      {
   3:          public DelegateTest() { }
   4:          
   5:          public void Go()
   6:          {
   7:              string strImportantInfo = "XYZ";
   8:              MatchEvaluator oEval = GetEvaluator(strImportantInfo);
   9:              string strResult = Regex.Replace("This Is Test Content", "Test", oEval, RegexOptions.IgnoreCase);
  10:          }
  11:   
  12:          private MatchEvaluator GetEvaluator(string strImportantStuff)
  13:          {
  14:              //Return a MatchEvaluator that is defined using an anonymous delegate method.
  15:              //This allows us to have access to strImportantStuff within the delegate.
  16:              return new MatchEvaluator(delegate(Match m)
  17:              {
  18:                  string strTransformedText = string.Empty;
  19:                  strTransformedText = "ABC";
  20:                  strTransformedText += strImportantStuff;
  21:                  return strTransformedText;
  22:              }
  23:              );
  24:          }
  25:      }

It worked perfectly! I had access to my local objects from within the delegate for the MatchEvaluator as long as I passed them into the creatoin method. No need to refactor my design. Yip-Yip.

Tuesday, August 5, 2008

Stupid Lazy Dev - Intro

A couple of months ago I found myself looking up a small trivial code snippet via a developers best friend. It was pry the 7th or 8th time I had looked it up in the past 3-4 years. It does not matter what it was, the point is for some reason certain coding tasks simply escape my memory over and over again. I don't know why this happens, it just does, and I don't think I'm alone.

Most of the time I find what I'm looking for and some times I do not. If not, that means I have to stumble through the .Net object model by hand or on MSDN. When I did find a blog post or MSDN article that answered my question I finally started bookmarking it so I could reference it later when I assuredly would once again forget how to do it or the syntax required. I figured I would start sharing these little
tidbits of obvious for two reasons. First, some of them were way more difficult to find then I think they should have been. I figure that if more people wrote about them it would be the first or second result instead of the 73rd. Second, I'm lazy and I want a single place I can come and look at these things and this will make me write them out in the way I want them written out and explained the way I want the explained (that's the Lazy part).

I'm going to call these "Stupid Lazy Dev" posts. Not because you are stupid or lazy if your reading them but because that's how I feel when I go to look these up for the umpteenth time.

Hopefully this will help others save some time and get back to doing something much more productive and important.........surfing the web.

Formatting C# code into HTML

Found this sweet post about a C# formatting engine that would spit out your code all nicely formatted for blogging. I have been looking for something like this for months to help out with this blog, yeah Derya! Thx for the info! Derya's WebResource.axd: Formatting C# code into HTML

Monday, August 4, 2008

14 Months Later...........

Ok, so work and life took over and this blog took a back seat. I have not posted anything in over a year but that does not mean I have not been thinking posting or trying to think up things to write about that may be useful.

I have been keeping a running list of topics and examples over the last year. I'll list them here and update them with links as I actually write the post. Hopefully this will server as motivation to me to keep them coming. And the list would be......

  • SharePoint Event Handler - ItemUpdated
  • SPUser.SID - How I've used it and issues
  • Running Elevated Privileges - How + Tips
  • Admin Page Creation
  • Features - Use/Deployment/Strats I have used
  • Getting a sites "Admins"
  • CAML - Oh what fun (Lists, Dates, Now, PubStartDate)
  • A Tighter Content Query WebPart
  • Delegate Parameter Hack - Regex example
  • Invoking Dynamic Objects - Reflection


  • I'll continue adding to this as I remember things. These are the ones I had plastered on my white board, now I just need to go back and look at my code and remember exactly why I thought it would be a good topic. If you see something that is listed that your interested in but the post has not been written, let me know! I'll make it the next one I do!

    Saturday, May 12, 2007

    Access Denied message when creating sub webs in MOSS 2007

    So we've been getting this Access Denied exception when creating MOSS 2007 sites in certain scenarios and its been kicking us in the happy place. To top it off, we've done a good bit of custom code that has been deployed and has caused similar Access Denied issues. We have since fixed our own custom code issues but just the fact that we had done custom stuff muddied the waters and had us going in 100 different directions when this latest problem came up.

    Here is the scenario we saw this issue appear:

    1. Create a site collection, I'll call it "SC1"
      1. Add some site collection admins and some users.
    2. Create a sub site under SC1 that does not inherit permissions, I'll call It "SubSite1"
      1. Add some users and create the site
      2. After the site is created, add a unique user that has "full control" on this site. Make sure the user has NO rights to the site collection SC1. I'll call him "SubSiteFullControl1"
    3. Log into SubSite1 as user SubSiteFullControl1
    4. Attempt to create a sub site from SubSite1
    5. We get Access Denied when you click the "Create" button on the form (newsbweb.aspx). Even though he has full control on the site.

    Searching through the MOSS log files we see the following two error messages:

    CreateSitePanel1: An exception occured in OnFormSave(). Exception: System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(Exception ex) at Microsoft.SharePoint.SPGlobal.HandleUnauthorizedAccessException(UnauthorizedAccessException ex) at Microsoft.SharePoint.Library.SPRequest.AddOrUpdateItem(String bstrUrl, String bstrListName, Boolean bAdd, Boolean bSystemUpdate, Boolean bPreserveItemVersion, Boolean bUpdateNoVersion, Int32& plID, String& pbstrGuid, Guid pbstrNewDocId, Boolean bHasNewDocId, String bstrVersion, Object& pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish) at Microsoft.SharePoint.SPListItem.AddOrUpdateItem(Boolean bAdd, Boolean bSystem, Boolean bPreserveItemVersion, Boolean bNoVersion, Boolean bMigration, Boolean bPublish, Boolean bCheckOut, Boolean bCheckin, Guid newGuidOnAdd, Int32& ulID, Object& objAttachmentNames, Object& objAttachmentContents, Boolean suppressAfterEvents) at Microsoft.SharePoint.SPListItem.UpdateInternal(Boolean bSystem, Boolean bPreserveItemVersion, Guid newGuidOnAdd, Boolean bMigration, Boolean bPublish, Boolean bNoVersion, Boolean bCheckOut, Boolean bCheckin, Boolean suppressAfterEvents) at Microsoft.SharePoint.SPListItem.Update() at Microsoft.SharePoint.Portal.SiteAdmin.CreateSitePanel1.AddItemToList(SPWeb web) at Microsoft.SharePoint.Portal.SiteAdmin.CreateSitePanel1.OnFormSave(Object objOfInterest)

    DelegateControl: Exception thrown in OnFormSave() method of child control for ControlId='CreateSitePanel1'. Exception: System.Threading.ThreadAbortException: Thread was being aborted. at Microsoft.SharePoint.Portal.SiteAdmin.CreateSitePanel1.OnFormSave(Object objOfInterest) at Microsoft.SharePoint.WebControls.DelegateControl.CallFormSaveForChildren(Object objOfInterest)

    Isn't that a nice little ditty. The short answer is it's trying to add a list item to some kind of share point list and getting blocked. We assumed that it must be at the Site Collection level since the user we are logged in as has no rights there, but we could not figure out WHY it would be trying to do this.

    As it turns out, at the Site Collection level you can check a box that says "Enforce listing new sites in Site Directory" (Located in SiteSettings->SiteDirectorySettings). That little punk is the culprit. If that is checked, every site that is created will attempt to add a new list item in the directory, REGUARDLESS of whether or not the user creating the site has rights too!!!

    Un-checking that box will fix the problem. Obviously MSFT needs to fix this as it's a totally valid case to have sub-web admins/full control users that don't have access to the site collection they belong too. Simply running the list item addition with elevated privileges would fix the problem. Here's hoping it's fixed in SP1.