NTLM login with Anonymous fallback

At work we run a fairly large extranet SharePoint farm where some sites are anonymous access. Normally in SharePoint, when a domain user on a domain workstation hits a SharePoint site that requires authentication, it automatically logs them in. Unfortunately, in that same scenario but the site is anonymous, the user will not get logged in. The user then has to realize this and click sign in at the top right. Also because SharePoint security trims the user interface so you only see what you have access to see.  Many times users will complain they have lost their permissions or something because they won’t be able to find their normal way of doing things when they don’t realize they aren’t logged in.

Several years ago I set out on a mission to fix this. The best solution I found was posted here: http://blogs.claritycon.com/blogs/ryan_powers/archive/2007/06/12/3187.aspx. I was able to take what he had done and modify it for SharePoint. So since authentication is tied to the session, we only need to run our code once per session. So the trick is determining when a new session is starting and running the code. I was unsuccessful in getting the SessionStateModule.Start event to fire and not fire when I needed it to so I decided to use the PostAcquireRequestState event and check on each request if I have already run my code or not. I’m using a session variable to ensure I only run the code once per session. Essentially what the code does is the first time a new session is started instead of outputting the normal html requested, the httpmodule outputs some javascript.

This javascript makes a web call to /_layouts/authenticate.aspx which is the page used to login you into SharePoint. This call is done in a way so if an error happens (such as not being able to login) that error is trapped and the end user never sees the gray login box. Whether the login is successful or not the javascript then refreshes the page. At this point the httpmodule will not change the html output because it’s already been run on the current session so the real requested html is sent and if the authentication was successful, the user is shown as being logged into the site.

Also notice I added the querystring Source=%2F%5Flayouts%2Fimages%2Fblank%2Egif to the authenticate url. Normally when authenticate.aspx is called without this querystring it will redirect the user to the current site’s homepage. Since this causes extra execution time running webparts and rendering the page as well as the end user never sees this page because the call is being done in javascript, I found it was much faster to send the user to an image after authenticating. So i chose the blank.gif which is available on any SharePoint installation. This requires much less resources on the server as well as showing the actual page faster to the browser.

I also do a few checks before I output the javascript. Since automatic ntlm login only works in IE and windows, I check the useragent for that condition. I also check to make sure the request isn’t for infopath form services (/_layouts/formserver.aspx) because we had some issues with the module not playing nice with those services.

Anyway, here’s the code:

public class MixedAuthenticationScreeningModule : IHttpModule
{
    private const string NO_SCRIPT_MESSAGE = "Your browser does not support JavaScript or has scripting disabled, which prevents credentials screening from working.";
    private string _requiresAuthenticationUrl = "/_layouts/Authenticate.aspx?Source=%2F%5Flayouts%2Fimages%2Fblank%2Egif";
 
    void IHttpModule.Init(HttpApplication context)
    {
        context.PostAcquireRequestState += new EventHandler(context_PostAcquireRequestState);
    }
 
    void context_PostAcquireRequestState(object sender, EventArgs e)
    {
        MixedModeLogin();
    }
 
    private void MixedModeLogin()
    {
        HttpContext context = HttpContext.Current;
        if (context.Session == null) return;
        if (context.Request.RequestType != "GET") return;
        if (context.Session["MixedModeAuth"] != null) return;
        context.Session["MixedModeAuth"] = false;
        if (IsWin32Ie(context) == false) return;
        if (context.Request.RawUrl.ToLower().Contains("/_layouts/formserver.aspx")) return;
        context.Session["MixedModeAuth"] = true;
        RenderScreeningHtml(context.Request.RawUrl, context.Response);
    }
 
    private bool IsWin32Ie(HttpContext context)
    {
        string userAgent = context.Request.UserAgent;
        return userAgent != null && userAgent.IndexOf("MSIE") >= 0 && userAgent.IndexOf("Windows") >= 0;
    }
 
    private void RenderScreeningHtml(string currentUrl, HttpResponse response)
    {
        string screeningFailedUrl = currentUrl;
        response.Cache.SetCacheability(HttpCacheability.NoCache);
        response.Cache.SetExpires(DateTime.Now.AddDays(-1)); //or a date much earlier than current time
        response.Write("<!--DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"--><html></html><head></head><script type="text/javascript" language="javascript">  function canAuthenticate() { try { var dom = new ActiveXObject("Msxml2.DOMDocument");  dom.async = false; dom.load("" + _requiresAuthenticationUrl + "");} catch(e) { return false; }  return true;}  canAuthenticate(); window.location.href=window.location.href; </script><noscript></noscript>");
        try
        {
            response.End();
        }
        catch { }
    }
 
    void IHttpModule.Dispose()
    {
    }
}

Here is what the user’s see when the javascript code is trying to log them in:

Here is what shows up in fiddler when someone is logged in successfully. Notice the first time default.aspx is called it’s only 763 bytes and the second time 61 KB. That’s because the first time only the javascript was sent to the browser, but the second time the whole html for the page was sent to the browser. You can also see where the authentication happens and since it was successful you can see where the blank.gif was loaded.

 

Here is what shows up in fiddler when someone is not logged in successfully, thus it falls back to anonymous. You can see that the authentication doesn’t happen and blank.gif is never called.

PowerPoint 2010 Save As Web Page

Recently a need came up for a user that wanted to post a PowerPoint slide with links embedded in it onto a SharePoint 2007 teamsite. Unfortunately, in Office 2010 the ability to Save As Web Page doesn’t exist anymore. It seems they are really promoting their PowerPoint services (Office Web Apps) and decided to remove this capability from the user interface. Fortunately, the backend code is still there to perform this function.

I found this forum post: http://social.technet.microsoft.com/Forums/en/officeappcompat/thread/89d70894-b455-4d3e-a801-f2574c3a0f5a talking about a quick way to save as html through the visual basic editor in PowerPoint. This was good but not very user friendly.

I decided to write my own PowerPoint Add-In to add a button in the ribbon that saves the presentation to html. Since this is my first add-in, it did take me a little while to get it working but I was really happy with the development experience.  Hitting F5 compiled, built and deployed my add-in and then started powerpoint for me.  This made debuging and testing really quick.

To get started I fired up Visual Studio 2010 and created a new PowerPoint 2010 Add-In. I then added a new item and selected Ribbon. I also added a button to the ribbon from the toolbox and created an on-click event for it. Below is the code for the on-click event:

SaveFileDialog saveFileDialog1 = new SaveFileDialog();

saveFileDialog1.Filter = "htm files (*.htm)|*.htm|All files (*.*)|*.*";
saveFileDialog1.FilterIndex = 0;
saveFileDialog1.RestoreDirectory = true;
saveFileDialog1.AddExtension = true;
saveFileDialog1.AutoUpgradeEnabled = true;
saveFileDialog1.CheckFileExists = false;
saveFileDialog1.CheckPathExists = true;
saveFileDialog1.DefaultExt = "htm";

if (saveFileDialog1.ShowDialog() != DialogResult.OK) return;

string filepath = saveFileDialog1.FileName;

Globals.SaveToHtml.Application.ActivePresentation.SaveAs(filepath, Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsHTML, Microsoft.Office.Core.MsoTriState.msoFalse);

 

Here’s a screenshot of what it looks like:

 

One of the reasons it took me a while was I had issues getting it installed on another machine.  It turns out I didn’t select Microsoft Visual Studio 2010 Tools for Office Runtime as a prerequisite to install under the publish settings.  Once that was checked it installed fine on other machines.

Guy’s Trip 2010

DSC06794Every other year my Dad treats my brothers and I on a guy’s trip. In 2006 we backpacked in Glacier National Park and went fly fishing (pictures here). In 2008 we went to they Smokey Mountains and fly fished (pictures here).  Also in 2008, the wives (Mom, Erica and Megan) started having their own girls trip.  In 2010, it was my turn to plan the trip and I wanted to take them to a place that Erica and I love to go to: The New River Gorge and Summersville Lake.  The wives and babies went on their own trip while we were in West Virginia, click here to read about their trip.
DSCF0766On thursday Brent drove up from Atlanta, David flew in from Montana and I drove from Raleigh to meet up in Winston-Salem, NC at my parents house. We got up at 5:30 am on friday and were on the road at 6 for the 3.5 hr drive to Summersville Lake for a day of rock climbing. We stopped and ate breakfast at a Chick-fila in Beckley, WV on the way up and were at Summersville Lake by 9:30am. We got our gear ready and started hiking along the trail, descended the ladder, scrambled across the rocks and eventually made it to the climb I wanted to start on, Hippie Dreams (5.7). Brent had the most climbing experience other than me so I had him belay me. But he hadn’t ever lead belayed anyone. I showed him how to do it on the grigri and had David back him up.
DSCF0775 I lead the route and then Brent top-roped it without any issues. Dad was up next and got sucked into the corner a little bit too much and his legs were pumped by the time he reached the first ledge about 30 ft up.
DSCF0781Next was David’s turn and he cruised it. I toproped it and cleaned it and we moved over to another 5.7, That Eight. I got to the second bolt and just couldn’t make the move I felt like I needed to make. I asked Brent to take up the rope and I rested. I think I didn’t have full confidence in Brent belaying me until I had him take, that’s why I couldn’t make the move I needed to. After a minute I had more confidence and made the move without any problems and finished the route. David went next and completely cruised the route. Dad went next and had a hard time getting past the first bolt. Brent went last and but it took him several tries to get past the crux. Once past it though he was too tired to continue. I decided to pull the rope and lead the route again to get the redpoint, which was a success, so I cleaned and rapped down.
DSCF0787We then moved one more route over to do Sniff the Drill (5.8). With my new confidence in my belayer, I was able to cruise this route.
DSCF0785David gave it a go but had a hard time getting through a blank section at the second bolt. He eventually came back down and Brent gave it a go. After a while, Brent did figure it out but then had another hard time getting past the third bolt. He eventually got it worked out and finished the route. David then wanted another go at it through some time and effot he was able to work out his beta and complete the route as well. I toproped the route and tried a few different ways to get past the second and third bolts and tried to compare it to the beta they had worked out. I cleaned it, rapped down and we decided to be done for the day. We went and checked in at the campground, setup our tent and drove to the Walmart in Summersville to get our fishing licenses for the next day. Afterwards we ate at Applebees.
DSC07879On Saturday we drove to the Four Seasons Outfitters in Richwood, WV to get some flies and information on the local fly fishing. They said the water was low so our best bet was to fish on the North fork of the Cherry river.
DSC07881We drove to the first bridge and dropped Brent and Dad off. The plan was for them to fish to the second bridge (less than 2 miles) and David and I would leave the car at the second bridge for Dad and Brent to pick up and drive upstream to meet up with David and I. David used a dry adam and I also used a dry adam and dropped a nymph. David caught a few small trout and and I caught a minnow with my nymph. The river seemed to get shallow so David and I walked back to the car but
DSCF0789Brent and Dad weren’t there yet. So we started walking downstream to meet up with them.
DSCF0795After another hour we didn’t run into Brent and Dad yet so David and I walked back to the car and drove it up downstream yelling out the window for Dad and Brent. Eventually David hopped out and looked downstream and found them. We decided to drive down to Fayetteville and eat at Pies and Pints before checking into the campground at Cantrells.
IMG_8540Sunday morning we had to be ready to raft the Upper Gauley at 7:30 am. I rafted the Upper several years ago with my Sunday School class from church. Brent’s been whitewater rafting in the past few years as well but Dad hasn’t been in 20 years and David has never been.
IMG_8542The Upper Gauley is rated in the top 5 most technical rivers in the world and has 5 class V+ rapids. Class V+ is the most difficult rapid that companies in the US are allowed to guide on. We started out hitting a few class III and IV rapids and then we hit the first V+ called Insignificant. After seeing the first drop all of us started being very timid in our paddling which caused us to hit it sideways
IMG_8550and everyone but the guide was knocked out of the raft. I instantly got my feet up in the defensive position like we were taught on the bus ride to the river that morning. The rest of the rapid I had to do in this position or trying to swim to the side to miss the undercut rocks. I felt like I kept getting dunked in the
IMG_8565water and every time I came back up I took a breath that was half filled with water. After I got to the flat water I swam to the left to get picked up by our guide in the raft. But my dad was unable to get over and another boat paddled quickly to catch up to him before he went down another set of rapids. After we all got back into the raft and finished coughing up the river water, we assessed what went wrong and we decided we would paddle better. We went over a few more class ||| and IV rapids on which one of them had a lot of people on the side out of the boats and we saw a park ranger. I asked our guide about it and he said he didn’t really want to think about it because there was lots of undercut rock there.
IMG_8569We later found out that somone had died there earlier that morning. The next class V+ rapid was called Pillow Rock. I was determined to paddle better this time because I didn’t want to swim any more rapids. Things seemed to be going well, we bounced off of the pillow rock and were almost done but Brent, David, and I got dumped out at the last drop. For me, this wasn’t a big deal because the water was flat pretty much right after that. Another raft picked me up and brought me to my raft where I saw David wasn’t there yet and another raft was telling our guide that he was stuck in the hydraulic or toilet bowl but his head was up. They tossed him a rope and was able to pull him out.
IMG_8573When he got onto our raft he had lost his shoe and was saying he couldn’t do this anymore and wanted to hike out. Somone soon found his shoe and we told him that we were in the middle of nowhere and he had to continue. When we assessed what went wrong, it turns out that my dad had fallen out earlier in the rapid and was hanging onto the side of the raft. When the guide noticed this, he tried to pull him back in and was unable to steer the boat to hit the last rapid successfully. The rest of the day went off without a hitch, including the 14 ft waterfall which is the last class V+ of the trip. I also decided to cliff jump at a particular section but no one else in my family would join me. They all said they had swam enough for the day.

We were planning on hiking the long point trail when we got back and taking a group photo but we got back so late we had to go ahead and head back to Winston-Salem. It was an adventurous trip where I had a great time spending with my dad and brothers. Luckily, I don’t think any of them developed any PTSD after the rafting incident. I can’t wait to see what we do in another 2 years. If you want to see more pictures from our trip, click here.

Adding a Camelbak to the Kelty FC3

Some climbing friends of ours gave us a Kelty Kid Carrier FC 3.0 when we had our baby shower. If you aren’t aware, it’s a backpack that you can carry your gear as well as your child in. Everything on the pack seemed really well thought out and was really nice except I couldn’t find a place for a camelbak. Anyone that knows me knows that I try to hydrate a lot. When we go climbing I usually have 4.5 liters of water just for me for the day; maybe a little less if it isn’t hot. So through some playing around I was able to work out a system where I could attach a camelbak bladder to the pack and still not lose any functionality or storage.

First off, I found some space to store the bladder behind where the child sits. If you notice in this picture you can see the handle and above the handle you can see there is the frame and it’s pretty beefy. I then accessed the frame and the space by unvelcroing and unsnapping the area above the lower storage. I then found some extra webbing I had around the house and wrapped the webbing around the top of the frame between the frame and the frabric shell of the pack. Make sure your webbing fits the plastic hook on the camelbak before attaching it to the pack. Since I am a climber I used a figure 8 knot to create a loop in the webbing but you can use an overhand or whatever knot you feel comfortable with. This loop is used to hang the camelbak bladder off of. This part was a little difficult because I had to do all of this without being able to see what my hands, the webbing or the knot.

Next I used the plastic hook on the camelbak bladder to attach the bladder to the loop I made with the webbing. This again takes a little time because you have to feel around and you can’t see your hands, the hook, or the loop while doing this. After it’s attached, I made sure the bladder is inside the opening and re-attched the velcro and snaps. The tube from the bladder goes through the velcro and I wrapped it around to the front strap. Then you are good to go!

SPCalendarView not working as expected in SharePoint Foundation 2010

I’m currently working to make sure all of my custom code works correctly in SharePoint Foundation 2010. One of my webparts utilized the SPCalendarView and I would set the datasource to be a SPCalendarItemCollection and it would render a calendar with the events I specified in the collection. Well, this no longer works in SharePoint Foundation 2010. I was able to achieve a workaround with a little help from reflector. You’ll need to create two classes:

public class MySPCalendarDataSource : Control, System.Web.UI.IDataSource
    {
        private MyCalendarSourceView _view;
        private static readonly object EventDataSourceChanged = new object();
        private SPCalendarItemCollection _data = null;

        public MySPCalendarDataSource(SPCalendarItemCollection data)
            : base()
        {
            _data = data;
        }

        event EventHandler System.Web.UI.IDataSource.DataSourceChanged
        {
            add
            {
                base.Events.AddHandler(EventDataSourceChanged, value);
            }

            remove
            {
                base.Events.RemoveHandler(EventDataSourceChanged, value);
            }
        }

        protected DataSourceView GetView(string viewName)
        {
            return this.internalGetView(viewName);
        }

        protected ICollection GetViewNames()
        {
            return new string[] { this.internalGetView(null).Name };
        }

        private MyCalendarSourceView internalGetView(string viewName)
        {
            if (this._view == null)
            {
                this._view = new MyCalendarSourceView(this, viewName, _data);
            }
            return this._view;
        }

        private void OnDataSourceChanged(EventArgs e)
        {
            EventHandler handler = (EventHandler)base.Events[EventDataSourceChanged];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void RaiseDataSourceChangedEvent(EventArgs e)
        {
            this.OnDataSourceChanged(e);
        }

        DataSourceView System.Web.UI.IDataSource.GetView(string viewName)
        {
            return this.GetView(viewName);
        }

        ICollection System.Web.UI.IDataSource.GetViewNames()
        {
            return this.GetViewNames();
        }        
    }
public class MyCalendarSourceView : DataSourceView
{
	SPCalendarItemCollection _data;

	public MyCalendarSourceView(System.Web.UI.IDataSource ds, string viewName, SPCalendarItemCollection data) : base(ds, viewName)
	{
		_data = data;
	}

	protected override System.Collections.IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
	{
		return _data;
	}
}

And where in SharePoint 2007 you could do this:

SPCalendarItemCollection items = GetCalendarItems();
SPCalendarView calView = new SPCalendarView();
calView.DataSource = items;
calView.DataBind();

You now have to do this:

SPCalendarItemCollection items = GetCalendarItems();
SPCalendarView calView = new SPCalendarView();
calView.EnableV4Rendering = false;
calView.DataSource = new MySPCalendarDataSource(items);
calView.DataBind();

Also, please note that for this to work, we do need to disable the V4 ajax rendering.