Commits

Jeff Craig  committed 9009fe4 Merge

Merge Jason and Dave's Branches

  • Participants
  • Parent commits 7fd5bc1, 9b5c2ea

Comments (0)

Files changed (49)

 # These lines are suggested according to the svn:ignore property
 # Feel free to enable them by uncommenting them
 syntax:glob
+
+*.suo
+*.user
+CodeCamp\CodeCampConfig\Configuration.cs
+
+*.suo
+*.user
+
+bin/*
+Debug/*
+Release/*

File CodeCamp/CodeCamp.sln

 		{CA90C5A3-E8CA-4B1A-B4F4-62D4DB898FA2} = {CA90C5A3-E8CA-4B1A-B4F4-62D4DB898FA2}
 	EndProjectSection
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{08CD0BF0-0ACD-43CF-BDC5-5B0BA0FC63A9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCampConfig", "CodeCampConfig\CodeCampConfig.csproj", "{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCampRepository", "CodeCampRepository\CodeCampRepository.csproj", "{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCampEntities", "CodeCampEntities\CodeCampEntities.csproj", "{D18977EA-CA74-4428-B01D-A7DD1466C3A6}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		{C409E3FB-9678-461A-8718-E99ED1F9E90F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{C409E3FB-9678-461A-8718-E99ED1F9E90F}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
 		{C409E3FB-9678-461A-8718-E99ED1F9E90F}.Release|x86.ActiveCfg = Release|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}.Release|x86.ActiveCfg = Release|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}.Release|x86.ActiveCfg = Release|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{D18977EA-CA74-4428-B01D-A7DD1466C3A6}.Release|x86.ActiveCfg = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		{6D48DAFA-0506-44B4-9D27-2A59EA65A0DA} = {0A16A38B-B5A8-4750-A30F-A2814E76C9A6}
 		{CA90C5A3-E8CA-4B1A-B4F4-62D4DB898FA2} = {0A16A38B-B5A8-4750-A30F-A2814E76C9A6}
 		{C409E3FB-9678-461A-8718-E99ED1F9E90F} = {0A16A38B-B5A8-4750-A30F-A2814E76C9A6}
+		{BDE6C8E9-9E07-468F-8B3D-AD99430813C2} = {08CD0BF0-0ACD-43CF-BDC5-5B0BA0FC63A9}
 	EndGlobalSection
 EndGlobal

File CodeCamp/CodeCamp/CodeCamp.csproj

     <Reference Include="System.Xml.Linq" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="AccountController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
     <Compile Include="Controllers\CodeCampData00.designer.cs">
       <DependentUpon>CodeCampData00.dbml</DependentUpon>
       <DesignTime>True</DesignTime>
     <Compile Include="Controllers\SponsorsHelper.cs" />
     <Compile Include="Controllers\TransformEntities00ToEntities01.cs" />
     <Compile Include="Controllers\VolunteersController.cs" />
+    <Compile Include="DevSatController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
     <Compile Include="Global.asax.cs">
       <DependentUpon>Global.asax</DependentUpon>
     </Compile>
+    <Compile Include="HomeController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
     <Compile Include="Models\AccountModels.cs" />
     <Compile Include="Models\DevSat\DevSatIndexViewModel.cs" />
     <Compile Include="Models\Home\HomeAboutViewModel.cs" />
     <Compile Include="Models\Sponsors\SponsorsListViewModel.cs" />
     <Compile Include="Models\Volunteers\VolunteerIndexViewModel.cs" />
     <Compile Include="Models\Volunteers\VolunteerListViewModel.cs" />
+    <Compile Include="PresentersController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ScheduleController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
+    <Compile Include="SessionsController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
+    <Compile Include="SharedController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
+    <Compile Include="SponsorsController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
+    <Compile Include="T4MVC.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
     <Compile Include="Views\Volunteers\DateTimeFormatter.cs" />
+    <Compile Include="VolunteersController.generated.cs">
+      <DependentUpon>T4MVC.tt</DependentUpon>
+    </Compile>
   </ItemGroup>
   <ItemGroup>
     <Content Include="Content\2010\Sponsors\Apress.png" />
       <LastGenOutput>CodeCampData02.designer.cs</LastGenOutput>
       <SubType>Designer</SubType>
     </None>
+    <None Include="T4MVC.settings.t4" />
+    <None Include="T4MVC.tt">
+      <Generator>TextTemplatingFileGenerator</Generator>
+      <LastGenOutput>T4MVC.cs</LastGenOutput>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="Controllers\CodeCampData00.dbml.layout">
   </ItemGroup>
   <ItemGroup>
     <Service Include="{3259AA49-8AA1-44D3-9025-A0B520596A8C}" />
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Controllers\CodeCampData01.dbml.layout">
       <DependentUpon>CodeCampData02.dbml</DependentUpon>
     </None>
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\CodeCampConfig\CodeCampConfig.csproj">
+      <Project>{BDE6C8E9-9E07-468F-8B3D-AD99430813C2}</Project>
+      <Name>CodeCampConfig</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\CodeCampRepository\CodeCampRepository.csproj">
+      <Project>{EE998DA6-A7DC-48DC-9F57-DE121597CAD4}</Project>
+      <Name>CodeCampRepository</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

File CodeCamp/CodeCamp/Controllers/AccountController.cs

 {
 
   [HandleError]
-  public class AccountController : Controller
+    public partial class AccountController : Controller
   {
 
     // This constructor is used by the MVC framework to instantiate the controller using
       private set;
     }
 
-    public ActionResult SignIn()
+    public virtual ActionResult SignIn()
     {
 
       return View();
     [AcceptVerbs( HttpVerbs.Post )]
     [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
         Justification = "Needs to take same parameter type as Controller.Redirect()" )]
-    public ActionResult SignIn( string userName, string password, bool rememberMe, string returnUrl )
+    public virtual ActionResult SignIn(string userName, string password, bool rememberMe, string returnUrl)
     {
 
       if ( !ValidateLogOn( userName, password ) )
       }
     }
 
-    public ActionResult SignOut()
+    public virtual ActionResult SignOut()
     {
 
       FormsAuth.SignOut();
       return RedirectToAction( "Index", "Home" );
     }
 
-    public ActionResult Create()
+    public virtual ActionResult Create()
     {
 
       ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
     }
 
     [AcceptVerbs( HttpVerbs.Post )]
-    public ActionResult Create( string userName, string email, string password, string confirmPassword )
+    public virtual ActionResult Create(string userName, string email, string password, string confirmPassword)
     {
 
       ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
     }
 
     [Authorize]
-    public ActionResult ChangePassword()
+    public virtual ActionResult ChangePassword()
     {
 
       ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
     [AcceptVerbs( HttpVerbs.Post )]
     [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
         Justification = "Exceptions result in password not being changed." )]
-    public ActionResult ChangePassword( string currentPassword, string newPassword, string confirmPassword )
+    public virtual ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
     {
 
       ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
       }
     }
 
-    public ActionResult ChangePasswordSuccess()
+    public virtual ActionResult ChangePasswordSuccess()
     {
 
       return View();

File CodeCamp/CodeCamp/Controllers/DevSatController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class DevSatController : Controller
+    public partial class DevSatController : Controller
   {
     // GET: /DevSat/
 
-    public ActionResult Index( string eventName )
+        public virtual ActionResult Index(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
 
       var model =
         new DevSatIndexViewModel

File CodeCamp/CodeCamp/Controllers/HomeController.cs

 namespace CodeCamp.Controllers
 {
   [HandleError]
-  public class HomeController : Controller
+    public partial class HomeController : Controller
   {
-    public ActionResult Index( string eventName )
+      public virtual ActionResult Index(string eventName)
     {
       (new TransformEntities00ToEntities01()).Execute();
 
       return View( model );
     }
 
-    public ActionResult About( string eventName )
+      public virtual ActionResult About(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
 
       var model =
         new HomeAboutViewModel

File CodeCamp/CodeCamp/Controllers/PresentersController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class PresentersController : Controller
+    public partial class PresentersController : Controller
   {
     #region List actions
 
     // GET: /Presenters/List
 
-    public ActionResult List( string eventName )
+        public virtual ActionResult List(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
       var eventPresenters = 
-        repository.GetPresenters( eventId )
+        repository.GetPresenters( eventRecord.Id )
         .Where( p => p.SessionPresenters.Count > 0 )
         .OrderBy( p => p.LastName )
         .ThenBy( p => p.FirstName );
 
-      var userPresenterId = repository.GetPresenter( eventId, User.Identity.Name );
+      var userPresenterId = repository.GetPresenter( eventRecord.Id, User.Identity.Name );
 
       var presenters = new List<PresentersListViewModel.Presenter>();
       foreach ( var presenter in eventPresenters )
 
     // GET: /Presenters/Details/5
 
-    public ActionResult Details( int id, string eventName )
+        public virtual ActionResult Details(int id, string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
       var presenter = repository.GetPresenter( id );
 
       var sessions =
     // GET: /Presenters/Add
 
     [Authorize]
-    public ActionResult Add( string eventName )
+    public virtual ActionResult Add(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var model = new PresentersAddViewModel
       {
 
     [AcceptVerbs( HttpVerbs.Post )] 
     [Authorize]
-    public ActionResult Add( PresentersAddViewModel model, string eventName )
+    public virtual ActionResult Add(PresentersAddViewModel model, string eventName)
     {
       try
       {
     // GET: /Presenters/Edit/5
 
     [Authorize]
-    public ActionResult Edit( string eventName )
+    public virtual ActionResult Edit(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
-      var presenter = repository.GetPresenter( eventId, User.Identity.Name );
+      var presenter = repository.GetPresenter( eventRecord.Id, User.Identity.Name );
 
       // verify that user is authorized to edit this presenter profile
       if ( !IsUserAuthorizedToEdit( presenter ) )
 
     [AcceptVerbs( HttpVerbs.Post )]
     [Authorize]
-    public ActionResult Edit( PresentersEditViewModel model, string eventName )
+    public virtual ActionResult Edit(PresentersEditViewModel model, string eventName)
     {
       try
       {

File CodeCamp/CodeCamp/Controllers/ScheduleController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class ScheduleController : Controller
+    public partial class ScheduleController : Controller
   {
 
-    public ActionResult Agenda( string eventName )
+        public virtual ActionResult Agenda(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
 
       var model = new ScheduleAgendaViewModel { EventDisplayName = eventDisplayName };
 
     }
 
 
-    public ActionResult Index( string eventName )
+        public virtual ActionResult Index(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var timeslotList = new List<ScheduleIndexViewModel.Timeslot>();
-      var timeslots = repository.GetTimeslots( eventId );
+      var timeslots = repository.GetTimeslots( eventRecord.Id );
       foreach ( var timeslot in timeslots )
       {
         var sessionList = new List<ScheduleIndexViewModel.Timeslot.Session>();
       return View( model );
     }
 
-    public ActionResult Rooms( string eventName )
+        public virtual ActionResult Rooms(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var buildings =
         ( 
-          from building in repository.GetBuildings( eventId )
+          from building in repository.GetBuildings( eventRecord.Id )
           orderby building.Name
           select new ScheduleRoomsViewModel.Building
           {
       return View( model );
     }
 
-    public ActionResult Room( string eventName, int id )
+        public virtual ActionResult Room(string eventName, int id)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var room = repository.GetRoom( id );
 
       return View( model );
     }
 
-    public ActionResult Hotels( string eventName )
+        public virtual ActionResult Hotels(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
 
       var model = new ScheduleHotelsViewModel { EventDisplayName = eventDisplayName };
 

File CodeCamp/CodeCamp/Controllers/SessionsController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class SessionsController : Controller
+    public partial class SessionsController : Controller
   {
     internal static string FilterDevSatTag( string sessionName )
     { return sessionName.StartsWith( "#devsat" ) ? sessionName.Substring( 8 ) : sessionName ; }
 
     // GET: /Sessions/List
 
-    public ActionResult List( string eventName )
+    public virtual ActionResult List(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var eventSessions =
-        repository.GetSessions( eventId )
+        repository.GetSessions( eventRecord.Id )
         .Where( s => !SqlMethods.Like( s.Title, "#devsat%" ) )
         .OrderBy( s => s.Title );
 
-      var userPresenter = repository.GetPresenter( eventId, User.Identity.Name );
+      var userPresenter = repository.GetPresenter( eventRecord.Id, User.Identity.Name );
 
       // assemble model
 
       {
         EventDisplayName = eventDisplayName,
 
-        EventTags = GetEventTags( eventId, repository ),
+        EventTags = GetEventTags( eventRecord.Id, repository ),
         Sessions = new List<SessionsListViewModel.Session>(),
 
         IsUserAuthenticated = User.Identity.IsAuthenticated,
 
     // GET: /Sessions/Details/5
 
-    public ActionResult Details( int id, string eventName )
+    public virtual ActionResult Details(int id, string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
+      var eventRecord = repository.GetEvent( eventName );
       var session = repository.GetSession( id );
 
       var model = new SessionsDetailsViewModel
       {
-        EventDisplayName = repository.GetEvent( eventId ).DisplayName,
+        EventDisplayName = eventRecord.DisplayName,
         SessionId = id,
         Title = FilterDevSatTag( session.Title ),
         Description = session.Description,
         SessionPresenters = GetSessionPresenters( session ),
         SessionTags = GetSessionTags( session ),
-        EventTags = GetEventTags( eventId, repository ),
+        EventTags = GetEventTags( eventRecord.Id, repository ),
         SessionMaterialsUrl = session.SessionMaterialsUrl,
         SessionScheduleInfo = GetSessionScheduleInfo( repository, session.Id ),
         IsUserAuthorizedToEdit = IsUserAuthorizedToEditSession( session )
     }
 
     [AcceptVerbs( HttpVerbs.Post )]
-    public ActionResult DetailsSetTags( SessionsDetailsViewModel model, int[] sessionTags )
+    public virtual ActionResult DetailsSetTags(SessionsDetailsViewModel model, int[] sessionTags)
     {
       SetSessionTags( model.SessionId, sessionTags );
       return RedirectToAction( "Details", new { id = model.SessionId } );
     }
 
     [AcceptVerbs( HttpVerbs.Post )]
-    public ActionResult DetailsAddTag( SessionsDetailsViewModel model, string newTagName )
+    public virtual ActionResult DetailsAddTag(SessionsDetailsViewModel model, string newTagName)
     {
       CreateTagAndAddToSession( model.SessionId, newTagName );
       return RedirectToAction( "Details", new { id = model.SessionId } );
     private static readonly bool isAddEnabled = false;
 
     [Authorize]
-    public ActionResult Add( string eventName )
+    public virtual ActionResult Add(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       if ( isAddEnabled )
       {
-        var presenter = repository.GetPresenter( eventId, User.Identity.Name );
+        var presenter = repository.GetPresenter( eventRecord.Id, User.Identity.Name );
         if ( presenter == null )
           return View( "AuthorizationError" );
 
 
     [AcceptVerbs( HttpVerbs.Post )]
     [Authorize]
-    public ActionResult Add( SessionsAddViewModel model, string eventName )
+    public virtual ActionResult Add(SessionsAddViewModel model, string eventName)
     {
       if ( !isAddEnabled )
         return RedirectToAction( "Add" );
     // GET: /Sessions/Edit/5
 
     [Authorize]
-    public ActionResult Edit( int id, string eventName )
+    public virtual ActionResult Edit(int id, string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
 
       var session = repository.GetSession( id );
 
 
     [AcceptVerbs( HttpVerbs.Post )]
     [Authorize]
-    public ActionResult Edit( SessionsEditViewModel model )
+    public virtual ActionResult Edit(SessionsEditViewModel model)
     {
       try
       {
 
     #region Tag action
 
-    public ActionResult Tag( int id, string eventName )
+    public virtual ActionResult Tag(int id, string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
+      var eventRecord = repository.GetEvent( eventName );
       var tag = repository.GetTag( id );
       var taggedSessions =
         from st in tag.SessionTags
 
       var model = new SessionsTagViewModel
       {
-        EventDisplayName = repository.GetEvent( eventId ).DisplayName,
+        EventDisplayName = eventRecord.DisplayName,
         ThisTag = new SessionsTagViewModel.Tag { Id = id, Name = tag.Name },
         Sessions = sessionsList,
-        EventTags = GetEventTags( eventId, repository )
+        EventTags = GetEventTags( eventRecord.Id, repository )
       };
 
       return View( "Tag", model );

File CodeCamp/CodeCamp/Controllers/SponsorsController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class SponsorsController : Controller
+    public partial class SponsorsController : Controller
   {
 
     
     // GET: /Sponsors/
 
-    public ActionResult List( string eventName )
+        public virtual ActionResult List(string eventName)
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
-      var eventSponsors = repository.GetSponsors( eventId );
+      var eventRecord = repository.GetEvent( eventName );
+      var eventDisplayName = eventRecord.DisplayName;
+      var eventSponsors = repository.GetSponsors( eventRecord.Id );
       
       // assemble model
       var sponsors = new List<SponsorsListViewModel.Sponsor>();
       sponsors.PermuteRandomly();
 
       var communitySponsors = new List<SponsorsListViewModel.CommunitySponsor>();
-      foreach ( var communitySponsor in repository.GetCommunitySponsors( eventId ) )
+      foreach ( var communitySponsor in repository.GetCommunitySponsors( eventRecord.Id ) )
         communitySponsors.Add(
           new SponsorsListViewModel.CommunitySponsor
           {
           } );
 
       var individualContributors = new List<SponsorsListViewModel.IndividualContributor>();
-      foreach ( var individualContributor in repository.GetIndividualContributors( eventId ) )
+      foreach ( var individualContributor in repository.GetIndividualContributors( eventRecord.Id ) )
         individualContributors.Add(
           new SponsorsListViewModel.IndividualContributor
           {
 
     // GET: /Sponsors/Details/5
 
-    public ActionResult Details( int id )
+        public virtual ActionResult Details(int id)
     {
       return View();
     }
 
     // GET: /Sponsors/Create
 
-    public ActionResult Create()
+        public virtual ActionResult Create()
     {
       return View();
     }
     // POST: /Sponsors/Create
 
     [HttpPost]
-    public ActionResult Create( FormCollection collection )
+        public virtual ActionResult Create(FormCollection collection)
     {
       try
       {
 
     // GET: /Sponsors/Edit/5
 
-    public ActionResult Edit( int id )
+    public virtual ActionResult Edit(int id)
     {
       return View();
     }
     // POST: /Sponsors/Edit/5
 
     [HttpPost]
-    public ActionResult Edit( int id, FormCollection collection )
+    public virtual ActionResult Edit(int id, FormCollection collection)
     {
       try
       {

File CodeCamp/CodeCamp/Controllers/VolunteersController.cs

 
 namespace CodeCamp.Controllers
 {
-  public class VolunteersController : Controller
+    public partial class VolunteersController : Controller
   {
 
     private VolunteerIndexViewModel GetVolunteerIndexViewModel( string eventName )
     {
-      int eventId = EventsHelper.GetEventId( eventName );
       var repository = new CodeCampDataRepository();
-      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;
+      var eventDisplayName = repository.GetEvent( eventName ).DisplayName;
 
       return new VolunteerIndexViewModel { EventDisplayName = eventDisplayName };
     }
 
     // Volunteer/Index
 
-    public ActionResult Index( string eventName )
+    public virtual ActionResult Index(string eventName)
     {
       return View( GetVolunteerIndexViewModel( eventName ) );
     }
 
     // Volunteer
 
-    public ActionResult List( string eventName )
+    public virtual ActionResult List(string eventName)
     {
       return View( "Index", GetVolunteerIndexViewModel( eventName ) );
     }

File CodeCamp/CodeCamp/Global.asax.cs

 using System.Web.Mvc;
 using System.Web.Routing;
 using CodeCamp.Controllers;
+using CodeCamp.Configuration;
 
 namespace CodeCampWeb
 {
         "{eventName}/{controller}/{action}/{id}",   // URL pattern with parameters
         new                                         // parameter defaults
         {
-          eventName = System.Configuration.ConfigurationManager.AppSettings["defaultEvent"],
+          eventName = SiteConfig.DefaultEvent,      // using new configuration project
           controller = "Home",
           action = "Index",
           id = string.Empty
         "{eventName}/{controller}/{action}/{id}",   // URL pattern with parameters
         new                                         // parameter defaults
         {
-          eventName = System.Configuration.ConfigurationManager.AppSettings["defaultEvent"],
+          eventName = SiteConfig.DefaultEvent,
           action = "List",
           id = string.Empty
         } );

File CodeCamp/CodeCamp/Models/Home/HomeAboutViewModel.cs

-using CodeCamp.Models.Shared;
+
+using CodeCamp.Models.Shared;
 
 namespace CodeCamp.Models.Home
 {

File CodeCamp/CodeCamp/T4MVC.settings.t4

+<#+
+/*
+
+This file contains settings used by T4MVC.tt. The main goal is to avoid the need for users
+to fork the 'official' template in order to achieve what they want.
+ 
+*/
+
+
+// The prefix used for things like MVC.Dinners.Name and MVC.Dinners.Delete(Model.DinnerID)
+const string HelpersPrefix = "MVC";
+
+// Namespaces to be referenced by the generated code
+readonly string[] ReferencedNamespaces = new string[] {
+};
+
+// The folder under the project that contains the areas
+const string AreasFolder = "Areas";
+
+// Choose whether you want to include an 'Areas' token when referring to areas.
+// e.g. Assume the Area is called Blog and the Controller is Post:
+// - When false use MVC.Blog.Post.etc...
+// - When true use MVC.Areas.Blog.Post.etc...
+static bool IncludeAreasToken = false;
+
+// The folder under the project that contains the controllers
+const string ControllersFolder = "Controllers";
+
+// The folder under the project that contains the views
+const string ViewsRootFolder = "Views";
+
+// The name of the interface that all T4MVC action results will implement
+const string ActionResultInterfaceName = "IT4MVCActionResult";
+
+// If true, the T4MVC action result interface will be generated
+// If false, the namespace of the interface must be referenced in the 'ReferencedNamespaces' setting
+const bool GenerateActionResultInterface = true;
+
+// If true, use lower case tokens in routes for the area, controller and action names
+const bool UseLowercaseRoutes = false;
+
+// The namespace that the links are generated in (e.g. "Links", as in Links.Content.nerd_jpg)
+const string LinksNamespace = "Links";
+
+// If true, links to static files include a query string containing the file's last change time. This way,
+// when the static file changes, the link changes and guarantees that the client will re-request the resource.
+// e.g. when true, the link looks like: "/Content/nerd.jpg?2009-09-04T12:25:48"
+const bool AddTimestampToStaticLinks = false;
+
+// Folders containing static files for which links are generated (e.g. Links.Scripts.Map_js)
+readonly string[] StaticFilesFolders = new string[] {
+    "Scripts",
+    "Content",
+};
+
+// Static files to exclude from the generated links   
+readonly string[] ExcludedStaticFileExtensions = new string[] {   
+    ".cs"  
+};  
+
+// If true, the template marks itself as unsaved as part of its execution.
+// This way it will be saved and update itself next time the project is built.
+// Basically, it keeps marking itself as unsaved to make the next build work.
+// Note: this is certainly hacky, but is the best I could come up with so far.
+static bool AlwaysKeepTemplateDirty = false;
+
+// If true,the template output will be split into multiple files.
+static bool SplitIntoMultipleFiles = true;
+
+void RenderAdditionalCode() {
+#>
+static class T4MVCHelpers {
+    // You can change the ProcessVirtualPath method to modify the path that gets returned to the client.
+    // e.g. you can prepend a domain, or append a query string:
+    //      return "http://localhost" + path + "?foo=bar";
+    private static string ProcessVirtualPathDefault(string virtualPath) {
+        // The path that comes in starts with ~/ and must first be made absolute
+        string path = VirtualPathUtility.ToAbsolute(virtualPath);
+        
+        // Add your own modifications here before returning the path
+        return path;
+    }
+
+    // Calling ProcessVirtualPath through delegate to allow it to be replaced for unit testing
+    public static Func<string, string> ProcessVirtualPath = ProcessVirtualPathDefault;
+
+
+    // Logic to determine if the app is running in production or dev environment
+    public static bool IsProduction() { 
+        return (HttpContext.Current != null && !HttpContext.Current.IsDebuggingEnabled); 
+    }
+}
+
+<#+
+}
+#>

File CodeCamp/CodeCamp/T4MVC.tt

+<#
+/*
+T4MVC Version 2.6.14
+Find latest version and documentation at http://mvccontrib.codeplex.com/wikipage?title=T4MVC
+Discuss on the T4MVC forum: http://forums.asp.net/1215.aspx
+
+T4MVC is part of the MvcContrib project (http://mvccontrib.codeplex.com)
+Maintained by David Ebbo, with much feedback from the MVC community (thanks all!)
+david.ebbo@microsoft.com
+http://twitter.com/davidebbo
+http://blogs.msdn.com/davidebb
+
+Related blog posts: http://blogs.msdn.com/davidebb/archive/tags/T4MVC/default.aspx
+
+Please use in accordance to the MvcContrib license (http://mvccontrib.codeplex.com/license)
+*/
+#>
+<#@ template language="C#v3.5" debug="true" hostspecific="true" #>
+<#@ assembly name="System.Core" #>
+<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
+<#@ assembly name="EnvDTE" #>
+<#@ assembly name="EnvDTE80" #>
+<#@ assembly name="VSLangProj" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="System.IO" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Text.RegularExpressions" #>
+<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
+<#@ import namespace="EnvDTE" #>
+<#@ import namespace="EnvDTE80" #>
+<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
+<#PrepareDataToRender(this); #>
+<#var manager = Manager.Create(Host, GenerationEnvironment); #>
+<#manager.StartHeader(); #>// <auto-generated />
+// This file was generated by a T4 template.
+// Don't change it directly as your change would get overwritten.  Instead, make changes
+// to the .tt file (i.e. the T4 template) and save it to regenerate this file.
+
+// Make sure the compiler doesn't complain about missing Xml comments
+#pragma warning disable 1591
+#region T4MVC
+
+using System;
+using System.Diagnostics;
+using System.CodeDom.Compiler;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Web;
+using System.Web.Hosting;
+using System.Web.Mvc;
+using System.Web.Mvc.Ajax;
+using System.Web.Mvc.Html;
+using System.Web.Routing;
+using <#=T4MVCNamespace #>;
+<#foreach (var referencedNamespace in ReferencedNamespaces) { #>
+using <#=referencedNamespace #>;
+<#} #>
+<#manager.EndBlock(); #>
+
+[<#= GeneratedCode #>, DebuggerNonUserCode]
+public static class <#=HelpersPrefix #> {
+<#if (IncludeAreasToken) { #>
+public static class Areas {
+<#} #>
+<#foreach (var area in Areas.Where(a => !string.IsNullOrEmpty(a.Name))) { #>
+    static readonly <#=area.Name #>Class s_<#=area.Name #> = new <#=area.Name #>Class();
+    public static <#=area.Name #>Class <#=EscapeID(area.Namespace) #> { get { return s_<#=area.Name #>; } }
+<#} #>
+<#if (IncludeAreasToken) { #>
+}
+<#} #>
+<#foreach (var controller in DefaultArea.GetControllers()) { #>
+    public static <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
+<#} #>
+}
+
+namespace <#=T4MVCNamespace #> {
+<#foreach (var area in Areas.Where(a => !string.IsNullOrEmpty(a.Name))) { #>
+    [<#= GeneratedCode #>, DebuggerNonUserCode]
+    public class <#=area.Name #>Class {
+        public readonly string Name = "<#=ProcessAreaOrControllerName(area.Name) #>";
+<#foreach (var controller in area.GetControllers()) { #>
+        public <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
+<#} #>
+    }
+<#} #>
+}
+
+namespace System.Web.Mvc {
+    [<#= GeneratedCode #>, DebuggerNonUserCode]
+    public static class T4Extensions {
+        public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result) {
+            return htmlHelper.RouteLink(linkText, result.GetRouteValueDictionary());
+        }
+
+        public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes) {
+            return ActionLink(htmlHelper, linkText, result, new RouteValueDictionary(htmlAttributes));
+        }
+
+        public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, IDictionary<string, object> htmlAttributes) {
+            return htmlHelper.RouteLink(linkText, result.GetRouteValueDictionary(), htmlAttributes);
+        }
+
+        public static MvcForm BeginForm(this HtmlHelper htmlHelper, ActionResult result, FormMethod formMethod) {
+            return htmlHelper.BeginForm(result, formMethod, null);
+        }
+
+        public static MvcForm BeginForm(this HtmlHelper htmlHelper, ActionResult result, FormMethod formMethod, object htmlAttributes) {
+            return BeginForm(htmlHelper, result, formMethod, new RouteValueDictionary(htmlAttributes));
+        }
+
+        public static MvcForm BeginForm(this HtmlHelper htmlHelper, ActionResult result, FormMethod formMethod, IDictionary<string, object> htmlAttributes) {
+            var callInfo = result.GetT4MVCResult();
+            return htmlHelper.BeginForm(callInfo.Action, callInfo.Controller, callInfo.RouteValueDictionary, formMethod, htmlAttributes);
+        }
+
+<#if (MvcVersion >= 2) {#>
+        public static void RenderAction(this HtmlHelper htmlHelper, ActionResult result) {
+            var callInfo = result.GetT4MVCResult();
+            htmlHelper.RenderAction(callInfo.Action, callInfo.Controller, callInfo.RouteValueDictionary);
+        }
+
+        public static MvcHtmlString Action(this HtmlHelper htmlHelper, ActionResult result) {
+            var callInfo = result.GetT4MVCResult();
+            return htmlHelper.Action(callInfo.Action, callInfo.Controller, callInfo.RouteValueDictionary);
+        }
+<#} #>
+        public static string Action(this UrlHelper urlHelper, ActionResult result) {
+            return urlHelper.RouteUrl(result.GetRouteValueDictionary());
+        }
+
+        public static string ActionAbsolute(this UrlHelper urlHelper, ActionResult result) {
+            return string.Format("{0}{1}",urlHelper.RequestContext.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority),
+                urlHelper.RouteUrl(result.GetRouteValueDictionary()));
+        }
+
+        public static <#=HtmlStringType #> ActionLink(this AjaxHelper ajaxHelper, string linkText, ActionResult result, AjaxOptions ajaxOptions) {
+            return ajaxHelper.RouteLink(linkText, result.GetRouteValueDictionary(), ajaxOptions);
+        }
+
+        public static <#=HtmlStringType #> ActionLink(this AjaxHelper ajaxHelper, string linkText, ActionResult result, AjaxOptions ajaxOptions, object htmlAttributes) {
+            return ajaxHelper.RouteLink(linkText, result.GetRouteValueDictionary(), ajaxOptions, new RouteValueDictionary(htmlAttributes));
+        }
+
+        public static <#=HtmlStringType #> ActionLink(this AjaxHelper ajaxHelper, string linkText, ActionResult result, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+            return ajaxHelper.RouteLink(linkText, result.GetRouteValueDictionary(), ajaxOptions, htmlAttributes);
+        }
+
+        public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result) {
+            return routes.MapRoute(name, url, result, (ActionResult)null);
+        }
+
+        public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults) {
+            return MapRoute(routes, name, url, result, defaults, null);
+        }
+
+        public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults, object constraints) {
+            // Start by adding the default values from the anonymous object (if any)
+            var routeValues = new RouteValueDictionary(defaults);
+
+            // Then add the Controller/Action names and the parameters from the call
+            foreach (var pair in result.GetRouteValueDictionary()) {
+                routeValues.Add(pair.Key, pair.Value);
+            }
+
+            var routeConstraints = new RouteValueDictionary(constraints);
+
+            // Create and add the route
+            var route = new Route(url, routeValues, routeConstraints, new MvcRouteHandler());
+            routes.Add(name, route);
+            return route;
+        }
+
+        public static <#=ActionResultInterfaceName #> GetT4MVCResult(this ActionResult result) {
+            var t4MVCResult = result as <#=ActionResultInterfaceName #>;
+            if (t4MVCResult == null) {
+                throw new InvalidOperationException("T4MVC methods can only be passed pseudo-action calls (e.g. MVC.Home.About()), and not real action calls.");
+            }
+            return t4MVCResult;
+        }
+
+        public static RouteValueDictionary GetRouteValueDictionary(this ActionResult result) {
+            return result.GetT4MVCResult().RouteValueDictionary;
+        }
+
+        public static ActionResult AddRouteValues(this ActionResult result, object routeValues) {
+            return result.AddRouteValues(new RouteValueDictionary(routeValues));
+        }
+
+        public static ActionResult AddRouteValues(this ActionResult result, RouteValueDictionary routeValues) {
+            RouteValueDictionary currentRouteValues = result.GetRouteValueDictionary();
+
+            // Add all the extra values
+            foreach (var pair in routeValues) {
+                currentRouteValues.Add(pair.Key, pair.Value);
+            }
+
+            return result;
+        }
+
+        public static ActionResult AddRouteValues(this ActionResult result, System.Collections.Specialized.NameValueCollection nameValueCollection) {
+            // Copy all the values from the NameValueCollection into the route dictionary
+            nameValueCollection.CopyTo(result.GetRouteValueDictionary());
+            return result;
+        }
+
+        public static ActionResult AddRouteValue(this ActionResult result, string name, object value) {
+            RouteValueDictionary routeValues = result.GetRouteValueDictionary();
+            routeValues.Add(name, value);
+            return result;
+        }
+        
+        public static void InitMVCT4Result(this <#=ActionResultInterfaceName #> result, string area, string controller, string action) {
+            result.Controller = controller;
+            result.Action = action;
+            result.RouteValueDictionary = new RouteValueDictionary();
+            <# if (Areas.Count > 1) { #>result.RouteValueDictionary.Add("Area", area ?? "");<# } #> 
+            result.RouteValueDictionary.Add("Controller", controller);
+            result.RouteValueDictionary.Add("Action", action);
+        }
+
+        public static bool FileExists(string virtualPath) {
+            if (!HostingEnvironment.IsHosted) return false;
+            string filePath = HostingEnvironment.MapPath(virtualPath);
+            return System.IO.File.Exists(filePath);
+        }
+
+        static DateTime CenturyBegin=new DateTime(2001,1,1);
+        public static string TimestampString(string virtualPath) {
+            if (!HostingEnvironment.IsHosted) return string.Empty;
+            string filePath = HostingEnvironment.MapPath(virtualPath);
+            return Convert.ToString((System.IO.File.GetLastWriteTimeUtc(filePath).Ticks-CenturyBegin.Ticks)/1000000000,16);            
+        }
+    }
+}
+
+<#if (GenerateActionResultInterface) { #>   
+[<#= GeneratedCode #>]   
+public interface <#=ActionResultInterfaceName #> {   
+    string Action { get; set; }   
+    string Controller { get; set; }   
+    RouteValueDictionary RouteValueDictionary { get; set; }   
+}   
+<#} #>  
+
+<#foreach (var resultType in ResultTypes.Values) { #>
+[<#= GeneratedCode #>, DebuggerNonUserCode]
+public class T4MVC_<#=resultType.Name #> : <#=resultType.FullName #>, <#=ActionResultInterfaceName #> {
+    public T4MVC_<#=resultType.Name #>(string area, string controller, string action): base(<#resultType.Constructor.WriteNonEmptyParameterValues(true); #>)  {
+        this.InitMVCT4Result(area, controller, action);
+    }
+    <#foreach (var method in resultType.AbstractMethods) { #> 
+    <#=method.IsPublic ? "public" : "protected" #> override void <#=method.Name #>(<#method.WriteFormalParameters(true); #>) { }
+    <#} #>
+
+    public string Controller { get; set; }
+    public string Action { get; set; }
+    public RouteValueDictionary RouteValueDictionary { get; set; }
+}
+<#} #>
+
+
+
+namespace <#=LinksNamespace #> {
+<#
+foreach (string folder in StaticFilesFolders) {
+    ProcessStaticFiles(Project, folder);
+}
+#>
+}
+
+<#
+RenderAdditionalCode();
+#>
+<#foreach (var controller in GetAbstractControllers().Where(c => !c.HasDefaultConstructor)) { #>
+<#manager.StartNewFile(controller.GeneratedFileName); #>
+namespace <#=controller.Namespace #> {
+    public partial class <#=controller.ClassName #> {
+        protected <#=controller.ClassName #>() { }
+    }
+}
+<#manager.EndBlock(); #>
+<#} #>
+
+<#foreach (var controller in GetControllers()) { #>
+<#
+    // Don't generate the file at all if the existing one is up to date
+    if (controller.GeneratedCodeIsUpToDate) {
+        manager.KeepGeneratedFile(controller.GeneratedFileName);
+        continue;
+    }
+#>
+<#manager.StartNewFile(controller.GeneratedFileName); #>
+<#if (!String.IsNullOrEmpty(controller.Namespace)) { #>
+namespace <#=controller.Namespace #> {
+<#} #>
+    public <#if (!controller.NotRealController) { #>partial <#} #>class <#=controller.ClassName #> {
+<#if (!controller.NotRealController) { #>
+<#if (!controller.HasExplicitConstructor) { #>
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public <#=controller.ClassName #>() { }
+
+<#} #>
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        protected <#=controller.ClassName #>(Dummy d) { }
+
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        protected RedirectToRouteResult RedirectToAction(ActionResult result) {
+            var callInfo = result.GetT4MVCResult();
+            return RedirectToRoute(callInfo.RouteValueDictionary);
+        }
+
+<#foreach (var method in controller.ActionMethodsUniqueWithoutParameterlessOverload) { #>
+        [NonAction]
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public <#=method.ReturnTypeFullName #> <#=method.Name #>() {
+            return new T4MVC_<#=method.ReturnType #>(Area, Name, ActionNames.<#=method.ActionName #>);
+        }
+<#} #>
+
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public <#=controller.ClassName #> Actions { get { return <#=controller.T4MVCControllerFullName #>; } }
+        [<#= GeneratedCode #>]
+        public readonly string Area = "<#=ProcessAreaOrControllerName(controller.AreaName) #>";
+        [<#= GeneratedCode #>]
+        public readonly string Name = "<#=ProcessAreaOrControllerName(controller.Name) #>";
+
+        static readonly ActionNamesClass s_actions = new ActionNamesClass();
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public ActionNamesClass ActionNames { get { return s_actions; } }
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public class ActionNamesClass {
+<#foreach (var method in controller.ActionMethodsWithUniqueNames) { #>
+<#  if (UseLowercaseRoutes) { #>
+            public readonly string <#=method.ActionName #> = (<#=method.ActionNameValueExpression #>).ToLowerInvariant();
+<#  } else { #>
+            public readonly string <#=method.ActionName #> = <#=method.ActionNameValueExpression #>;
+<#  }
+} #>
+        }
+
+<#} #>
+
+        static readonly ViewNames s_views = new ViewNames();
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public ViewNames Views { get { return s_views; } }
+        [<#= GeneratedCode #>, DebuggerNonUserCode]
+        public class ViewNames {
+<#RenderControllerViews(controller);#>
+        }
+    }
+
+<#if (!controller.NotRealController) { #>
+    [<#= GeneratedCode #>, DebuggerNonUserCode]
+    public class <#=controller.DerivedClassName #>: <#=controller.FullClassName #> {
+        public <#=controller.DerivedClassName #>() : base(Dummy.Instance) { }
+
+<#foreach (var method in controller.ActionMethods) { #>
+        public override <#=method.ReturnTypeFullName #> <#=method.Name #>(<#method.WriteFormalParameters(true); #>) {
+            var callInfo = new T4MVC_<#=method.ReturnType #>(Area, Name, ActionNames.<#=method.ActionName #>);
+<#if (method.Parameters.Count > 0) { #>
+<#foreach (var p in method.Parameters) { #>
+            callInfo.RouteValueDictionary.Add(<#=p.RouteNameExpression #>, <#=p.Name #>);
+<#} #>
+<#}#>
+            return callInfo;
+        }
+
+<#} #>
+    }
+<#} #>
+<#if (!String.IsNullOrEmpty(controller.Namespace)) { #>
+}
+<#} #>
+
+<#manager.EndBlock(); #>
+<#} #>
+
+
+namespace <#=T4MVCNamespace #> {
+    [<#= GeneratedCode #>, DebuggerNonUserCode]
+    public class Dummy {
+        private Dummy() { }
+        public static Dummy Instance = new Dummy();
+    }
+}
+
+<#manager.StartFooter(); #>
+#endregion T4MVC
+#pragma warning restore 1591
+<#manager.EndBlock(); #>
+<#manager.Process(SplitIntoMultipleFiles); #>
+
+<#@ Include File="T4MVC.settings.t4" #>
+
+<#+ 
+const string T4MVCNamespace = "T4MVC";
+const string ControllerSuffix = "Controller";
+
+static DTE Dte;
+static Project Project;
+static string AppRoot;
+static HashSet<AreaInfo> Areas;
+static AreaInfo DefaultArea;
+static Dictionary<string, ResultTypeInfo> ResultTypes;
+static TextTransformation TT;
+static string T4FileName;
+static string T4Folder;
+static string GeneratedCode = @"GeneratedCode(""T4MVC"", ""2.0"")";
+static float MvcVersion;
+static string HtmlStringType;
+static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
+
+IEnumerable<ControllerInfo> GetControllers() {
+    var controllers = new List<ControllerInfo>();
+
+    foreach (var area in Areas) {
+        controllers.AddRange(area.GetControllers());
+    }
+
+    return controllers;
+}
+
+IEnumerable<ControllerInfo> GetAbstractControllers() {
+    var controllers = new List<ControllerInfo>();
+
+    foreach (var area in Areas) {
+        controllers.AddRange(area.GetAbstractControllers());
+    }
+
+    return controllers;
+}
+
+void PrepareDataToRender(TextTransformation tt) {
+    TT = tt;
+    T4FileName = Path.GetFileName(Host.TemplateFile);
+    T4Folder = Path.GetDirectoryName(Host.TemplateFile);
+    Areas = new HashSet<AreaInfo>();
+    ResultTypes = new Dictionary<string, ResultTypeInfo>();
+
+    // Get the DTE service from the host
+    var serviceProvider = Host as IServiceProvider;
+    if (serviceProvider != null) {
+        Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
+    }
+
+    // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
+    if (Dte == null) {
+        throw new Exception("T4MVC can only execute through the Visual Studio host");
+    }
+
+    Project = GetProjectContainingT4File(Dte);
+
+    if (Project == null) {
+        Error("Could not find the VS Project containing the T4 file.");
+        return;
+    }
+
+    // Get the path of the root folder of the app
+    AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
+
+    MvcVersion = GetMvcVersion();
+
+    // Use the proper return type of render helpers
+    HtmlStringType = MvcVersion < 2 ? "string" : "MvcHtmlString";
+
+    ProcessAreas(Project);
+}
+
+float GetMvcVersion() {
+    var vsProject = (VSLangProj.VSProject)Project.Object;
+
+    foreach (VSLangProj.Reference r in vsProject.References) {
+        if (r.Name.Equals("System.Web.Mvc", StringComparison.OrdinalIgnoreCase)) {
+            return r.MajorVersion + (r.MinorVersion / 10);
+        }
+    }
+
+    // We should never get here, but default to v1 just in case
+    return 1;
+}
+
+Project GetProjectContainingT4File(DTE dte) {
+
+    // Find the .tt file's ProjectItem
+    ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
+
+    // If the .tt file is not opened, open it
+    if (projectItem.Document == null)
+        projectItem.Open(Constants.vsViewKindCode);
+
+    if (AlwaysKeepTemplateDirty) {
+        // Mark the .tt file as unsaved. This way it will be saved and update itself next time the
+        // project is built. Basically, it keeps marking itself as unsaved to make the next build work.
+        // Note: this is certainly hacky, but is the best I could come up with so far.
+        projectItem.Document.Saved = false;
+    }
+
+    return projectItem.ContainingProject;
+}
+
+void ProcessAreas(Project project) {
+    // Process the default area
+    ProcessArea(project.ProjectItems, null);
+
+    // Get the Areas folder
+    ProjectItem areaProjectItem = GetProjectItem(project, AreasFolder);
+    if (areaProjectItem == null)
+        return;
+
+    foreach (ProjectItem item in areaProjectItem.ProjectItems) {
+        if (IsFolder(item)) {
+            ProcessArea(item.ProjectItems, item.Name);
+        }
+    }
+}
+
+void ProcessArea(ProjectItems areaFolderItems, string name) {
+    var area = new AreaInfo() { Name = name };
+    ProcessAreaControllers(areaFolderItems, area);
+    ProcessAreaViews(areaFolderItems, area);
+    Areas.Add(area);
+
+    if (String.IsNullOrEmpty(name))
+        DefaultArea = area;
+}
+
+void ProcessAreaControllers(ProjectItems areaFolderItems, AreaInfo area) {
+    // Get area Controllers folder
+    ProjectItem controllerProjectItem = GetProjectItem(areaFolderItems, ControllersFolder);
+    if (controllerProjectItem == null)
+        return;
+
+    ProcessControllersRecursive(controllerProjectItem, area);
+}
+
+void ProcessAreaViews(ProjectItems areaFolderItems, AreaInfo area) {
+    // Get area Views folder
+    ProjectItem viewsProjectItem = GetProjectItem(areaFolderItems, ViewsRootFolder);
+    if (viewsProjectItem == null)
+        return;
+
+    ProcessAllViews(viewsProjectItem, area);
+}
+
+void ProcessControllersRecursive(ProjectItem projectItem, AreaInfo area) {
+
+    // Recurse into all the sub-items (both files and folder can have some - e.g. .tt files)
+    foreach (ProjectItem item in projectItem.ProjectItems) {
+        ProcessControllersRecursive(item, area);
+    }
+
+    if (projectItem.FileCodeModel != null) {
+        DateTime controllerLastWriteTime = File.GetLastWriteTime(projectItem.get_FileNames(0));
+        foreach (var type in projectItem.FileCodeModel.CodeElements.OfType<CodeClass2>()) {
+            ProcessControllerType(type, area, controllerLastWriteTime);
+        }
+        // Process all the elements that are namespaces
+        foreach (var ns in projectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>()) {
+            foreach (var type in ns.Members.OfType<CodeClass2>()) {
+                ProcessControllerType(type, area, controllerLastWriteTime);
+            }
+        }
+    }
+}
+
+void ProcessControllerType(CodeClass2 type, AreaInfo area, DateTime controllerLastWriteTime) {
+    // Only process types that end with Controller
+    // REVIEW: this check is not super reliable. Should look at base class.
+    if (!type.Name.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase))
+        return;
+
+    // Don't process generic classes (their concrete derived classes will be processed)
+    if (type.IsGeneric)
+        return;
+
+    // Make sure the class is partial
+    if (type.ClassKind != vsCMClassKind.vsCMClassKindPartialClass) {
+        try {
+            type.ClassKind = vsCMClassKind.vsCMClassKindPartialClass;
+        }
+        catch {
+            // If we couldn't make it partial, give a warning and skip it
+            Warning(String.Format("{0} was not able to make the class {1} partial. Please change it manually if possible", T4FileName, type.Name));
+            return;
+        }
+        Warning(String.Format("{0} changed the class {1} to be partial", T4FileName, type.Name));
+    }
+
+    // Collect misc info about the controller class and add it to the collection
+    var controllerInfo = new ControllerInfo {
+        Area = area,
+        Namespace = type.Namespace != null ? type.Namespace.Name : String.Empty,
+        ClassName = type.Name
+    };
+
+    // Check if the controller has changed since the generated file was last created
+    DateTime lastGenerationTime = File.GetLastWriteTime(controllerInfo.GeneratedFileFullPath);
+    if (lastGenerationTime > controllerLastWriteTime) {
+        controllerInfo.GeneratedCodeIsUpToDate = true;
+    }
+
+    // Either process new ControllerInfo or integrate results into existing object for partially defined controllers
+    var target = area.Controllers.Add(controllerInfo) ? controllerInfo : area.Controllers.First(c => c.Equals(controllerInfo));
+    target.HasExplicitConstructor |= HasExplicitConstructor(type);
+    target.HasExplicitDefaultConstructor |= HasExplicitDefaultConstructor(type);
+
+    if (type.IsAbstract) {
+        // If it's abstract, set a flag and don't process action methods (derived classes will)
+        target.IsAbstract = true;
+    }
+    else {
+        // Process all the action methods in the controller
+        ProcessControllerActionMethods(target, type);
+    }
+}
+
+void ProcessControllerActionMethods(ControllerInfo controllerInfo, CodeClass2 current) {
+
+    // We want to process not just the controller class itself, but also its parents, as they
+    // may themselves define actions
+    for (CodeClass2 type = current; type != null && type.FullName != "System.Web.Mvc.Controller"; type = (CodeClass2)type.Bases.Item(1)) {
+
+        // If the type doesn't come from this project, some actions on it will fail. Try to get a real project type if possible.
+        if (type.InfoLocation != vsCMInfoLocation.vsCMInfoLocationProject) {
+            // Go through all the projects in the solution
+            //foreach (Project prj in Dte.Solution.Projects) {
+            for (int i = 1; i <= Dte.Solution.Projects.Count; i++) {
+                Project prj = null;
+                try {
+                    prj = Dte.Solution.Projects.Item(i);
+                }
+                catch (System.Runtime.Serialization.SerializationException) {
+                    // Some project types (that we don't care about) cause a strange exception, so ingore it
+                    continue;
+                }
+
+                // Skip it if it's the current project or doesn't have a code model
+                if (prj == Project || prj.CodeModel == null)
+                    continue;
+
+                // If we can get a local project type, use it instead of the original
+                var codeType = prj.CodeModel.CodeTypeFromFullName(type.FullName);
+                if (codeType != null && codeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject) {
+                    type = (CodeClass2)codeType;
+                    break;
+                }
+            }
+        }
+
+        foreach (CodeFunction2 method in GetMethods(type)) {
+            // Ignore non-public methods
+            if (method.Access != vsCMAccess.vsCMAccessPublic)
+                continue;
+
+            // Ignore methods that are marked as not being actions
+            if (GetAttribute(method.Attributes, "System.Web.Mvc.NonActionAttribute") != null)
+                continue;
+
+            // This takes care of avoiding generic types which cause method.Type.CodeType to blow up
+            if (method.Type.TypeKind != vsCMTypeRef.vsCMTypeRefCodeType)
+                continue;
+
+            // We only support action methods that return an ActionResult derived type
+            if (!method.Type.CodeType.get_IsDerivedFrom("System.Web.Mvc.ActionResult")) {
+                Warning(String.Format("{0} doesn't support {1}.{2} because it doesn't return a supported ActionResult type", T4FileName, type.Name, method.Name));
+                continue;
+            }
+
+            // If we haven't yet seen this return type, keep track of it
+            if (!ResultTypes.ContainsKey(method.Type.CodeType.Name)) {
+                var resTypeInfo = new ResultTypeInfo(method.Type.CodeType);
+
+                ResultTypes[method.Type.CodeType.Name] = resTypeInfo;
+            }
+
+            // Make sure the method is virtual
+            if (!method.CanOverride && method.OverrideKind != vsCMOverrideKind.vsCMOverrideKindOverride) {
+                try {
+                    method.CanOverride = true;
+                }
+                catch {
+                    // If we couldn't make it virtual, give a warning and skip it
+                    Warning(String.Format("{0} was not able to make the action method {1}.{2} virtual. Please change it manually if possible", T4FileName, type.Name, method.Name));
+                    continue;
+                }
+                Warning(String.Format("{0} changed the action method {1}.{2} to be virtual", T4FileName, type.Name, method.Name));
+            }
+
+            // Collect misc info about the action method and add it to the collection
+            controllerInfo.ActionMethods.Add(new ActionMethodInfo(method));
+        }
+    }
+}
+
+void ProcessAllViews(ProjectItem viewsProjectItem, AreaInfo area) {
+    // Go through all the sub-folders in the Views folder
+    foreach (ProjectItem item in viewsProjectItem.ProjectItems) {
+
+        // We only care about sub-folders, not files
+        if (!IsFolder(item))
+            continue;
+
+        // Find the controller for this view folder
+        ControllerInfo controller = area.Controllers.SingleOrDefault(c => c.Name.Equals(item.Name, StringComparison.OrdinalIgnoreCase));
+
+        if (controller == null) {
+            // If it doesn't match a controller, treat as a pseudo-controller for consistency
+            controller = new ControllerInfo {
+                Area = area,
+                NotRealController = true,
+                Namespace = MakeClassName(T4MVCNamespace, area.Name),
+                ClassName = item.Name + ControllerSuffix
+            };
+            area.Controllers.Add(controller);
+        }
+
+        AddViewsRecursive(item.ProjectItems, controller.ViewsFolder);
+    }
+}
+
+void AddViewsRecursive(ProjectItems items, ViewsFolderInfo viewsFolder) {
+    // Go through all the files in the subfolder to get the view names
+    foreach (ProjectItem item in items) {
+        if (item.Kind == Constants.vsProjectItemKindPhysicalFile) {
+            if (Path.GetExtension(item.Name).Equals(".master", StringComparison.OrdinalIgnoreCase))
+                continue;	// ignore master files
+            viewsFolder.AddView(item);
+        }
+        else if (item.Kind == Constants.vsProjectItemKindPhysicalFolder) {
+            string folderName = Path.GetFileName(item.Name);
+            if (folderName.Equals("App_LocalResources", StringComparison.OrdinalIgnoreCase))
+                continue;
+            var subViewFolder = new ViewsFolderInfo() { Name = folderName };
+            viewsFolder.SubFolders.Add(subViewFolder);
+            AddViewsRecursive(item.ProjectItems, subViewFolder);
+        }
+    }
+}
+
+void RenderControllerViews(ControllerInfo controller) {
+    PushIndent("            ");
+    RenderViewsRecursive(controller.ViewsFolder, controller);
+    PopIndent();
+}
+
+void RenderViewsRecursive(ViewsFolderInfo viewsFolder, ControllerInfo controller) {
+
+    // For each view, generate a readonly string
+    foreach (var viewPair in viewsFolder.Views) {
+        WriteLine("public readonly string " + EscapeID(Sanitize(viewPair.Key)) + " = \"" + viewPair.Value + "\";");
+    }
+
+    // For each sub folder, generate a class and recurse
+    foreach (var subFolder in viewsFolder.SubFolders) {
+        string newClassName = Sanitize(subFolder.Name);#>
+static readonly _<#=newClassName#> s_<#=newClassName#> = new _<#=newClassName#>();
+public _<#=newClassName#> <#=EscapeID(newClassName)#> { get { return s_<#=newClassName#>; } }
+public partial class _<#=newClassName#>{
+<#+
+PushIndent("    ");
+RenderViewsRecursive(subFolder, controller);
+PopIndent();
+
+WriteLine("}");
+    }
+}
+
+void ProcessStaticFiles(Project project, string folder) {
+
+    ProjectItem folderProjectItem = GetProjectItem(project, folder);
+    if (folderProjectItem != null) {
+        ProcessStaticFilesRecursive(folderProjectItem, "~");
+    }
+}
+
+void ProcessStaticFilesRecursive(ProjectItem projectItem, string path) {
+
+    if (IsFolder(projectItem)) { #>
+    [<#= GeneratedCode #>, DebuggerNonUserCode]
+    public static class <#=EscapeID(Sanitize(projectItem.Name)) #> {
+        private const string URLPATH = "<#=path#>/<#=projectItem.Name#>";
+        public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
+        public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
+<#+
+PushIndent("    ");
+
+// Recurse into all the items in the folder
+foreach (ProjectItem item in projectItem.ProjectItems) {
+    ProcessStaticFilesRecursive(item, path + "/" + projectItem.Name);
+}
+
+PopIndent();
+#>
+    }
+
+<#+
+}
+    else { #>
+<#+
+if (!ExcludedStaticFileExtensions.Any(extension => projectItem.Name.EndsWith(extension, StringComparison.OrdinalIgnoreCase))) {
+    // if it's a non-minified javascript file
+    if (projectItem.Name.EndsWith(".js") && !projectItem.Name.EndsWith(".min.js")) { 
+        if (AddTimestampToStaticLinks) { #>
+    public static readonly string <#=Sanitize(projectItem.Name)#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=projectItem.Name.Replace(".js", ".min.js")#>") ? Url("<#=projectItem.Name.Replace(".js", ".min.js")#>")+"?"+T4Extensions.TimestampString(URLPATH + "/<#=projectItem.Name#>") : Url("<#=projectItem.Name#>")+"?"+T4Extensions.TimestampString(URLPATH + "/<#=projectItem.Name#>");
+        <#+} else {#>
+    public static readonly string <#=Sanitize(projectItem.Name)#> = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/<#=projectItem.Name.Replace(".js", ".min.js")#>") ? Url("<#=projectItem.Name.Replace(".js", ".min.js")#>") : Url("<#=projectItem.Name#>");
+        <#+}  #>          
+<#+}
+    else if (AddTimestampToStaticLinks) { #>
+    public static readonly string <#=Sanitize(projectItem.Name)#> = Url("<#=projectItem.Name#>")+"?"+T4Extensions.TimestampString(URLPATH + "/<#=projectItem.Name#>");
+<#+}
+    else { #>
+    public static readonly string <#=Sanitize(projectItem.Name)#> = Url("<#=projectItem.Name#>");
+<#+}
+} #>
+<#+
+// Non folder items may also have children (virtual folders, Class.cs -> Class.Designer.cs, template output)
+// Just register them on the same path as their parent item
+foreach (ProjectItem item in projectItem.ProjectItems) {
+    ProcessStaticFilesRecursive(item, path);
+}
+    }
+}
+ProjectItem GetProjectItem(Project project, string name) {
+    return GetProjectItem(project.ProjectItems, name);
+}
+
+ProjectItem GetProjectItem(ProjectItems items, string subPath) {
+
+    ProjectItem current = null;
+    foreach (string name in subPath.Split('\\')) {
+        try {
+            // ProjectItems.Item() throws when it doesn't exist, so catch the exception
+            // to return null instead.
+            current = items.Item(name);
+        }
+        catch {
+            // If any chunk couldn't be found, fail
+            return null;
+        }
+        items = current.ProjectItems;
+    }
+
+    return current;
+}
+
+static string GetVirtualPath(ProjectItem item) {
+    string fileFullPath = item.get_FileNames(0);
+    if (!fileFullPath.StartsWith(AppRoot, StringComparison.OrdinalIgnoreCase))
+        throw new Exception(string.Format("File {0} is not under app root {1}. Please report issue.", fileFullPath, AppRoot));
+
+    // Make a virtual path from the physical path
+    return "~/" + fileFullPath.Substring(AppRoot.Length).Replace('\\', '/');
+}
+
+static string ProcessAreaOrControllerName(string name) {
+    return UseLowercaseRoutes ? name.ToLowerInvariant() : name;
+}
+
+// Return all the CodeFunction2 in the CodeElements collection
+static IEnumerable<CodeFunction2> GetMethods(CodeClass2 codeClass) {
+    // Only look at regular method (e.g. ignore things like contructors)
+    return codeClass.Members.OfType<CodeFunction2>()
+        .Where(f => f.FunctionKind == vsCMFunction.vsCMFunctionFunction);
+}
+
+// Check if the class has any explicit constructor
+static bool HasExplicitConstructor(CodeClass2 codeClass) {
+    return codeClass.Members.OfType<CodeFunction2>().Any(
+        f => f.FunctionKind == vsCMFunction.vsCMFunctionConstructor);
+}
+
+// Check if the class has a default (i.e. no params) constructor
+static bool HasExplicitDefaultConstructor(CodeClass2 codeClass) {
+    return codeClass.Members.OfType<CodeFunction2>().Any(
+        f => f.FunctionKind == vsCMFunction.vsCMFunctionConstructor && f.Parameters.Count == 0);
+}
+
+// Find a method with a given name
+static CodeFunction2 GetMethod(CodeClass2 codeClass, string name) {
+    return GetMethods(codeClass).FirstOrDefault(f => f.Name == name);
+}
+
+// Find an attribute of a given type on an attribute collection
+static CodeAttribute2 GetAttribute(CodeElements attributes, string attributeType) {
+    for (int i = 1; i <= attributes.Count; i++) {
+        var attrib = (CodeAttribute2)attributes.Item(i);
+        if (attrib.FullName == attributeType) {
+            return attrib;
+        }
+    }
+    return null;
+}
+
+// Return whether a ProjectItem is a folder and not a file
+static bool IsFolder(ProjectItem item) {
+    return (item.Kind == Constants.vsProjectItemKindPhysicalFolder);
+}
+
+static string MakeClassName(string ns, string classname) {
+    return String.IsNullOrEmpty(ns) ? classname :
+        String.IsNullOrEmpty(classname) ? ns : ns + "." + codeProvider.CreateEscapedIdentifier(classname);
+}
+
+static string Sanitize(string token) {
+    // Replace all invalid chars by underscores
+    token = Regex.Replace(token, @"[\W\b]", "_", RegexOptions.IgnoreCase);
+
+    // If it starts with a digit, prefix it with an underscore
+    token = Regex.Replace(token, @"^\d", @"_$0");
+
+    // Check for reserved words
+    // TODO: Clean this up and add other reserved words (keywords, etc)
+    if (token == "Url") token = "_Url";
+
+    return token;
+}
+
+static string EscapeID(string id) {
+    return codeProvider.CreateEscapedIdentifier(id);
+}
+
+// Data structure to collect data about an area
+class AreaInfo {
+    public AreaInfo() {
+        Controllers = new HashSet<ControllerInfo>();
+    }
+
+    public string Name { get; set; }
+    public HashSet<ControllerInfo> Controllers { get; set; }
+
+    public string Namespace {
+        get {
+            // When *not* using an 'Areas' token, we need to disambiguate conflicts
+            // between Area names and controller names (from the default Area)
+            if (!IncludeAreasToken && DefaultArea.Controllers.Any(c => c.Name == Name))
+                return Name + "Area";
+
+            return Name;
+        }
+    }
+
+    public IEnumerable<ControllerInfo> GetControllers() {
+        return Controllers.Where(c => !c.IsAbstract);
+    }
+
+    public IEnumerable<ControllerInfo> GetAbstractControllers() {
+        return Controllers.Where(c => c.IsAbstract);
+    }
+}
+
+// Data structure to collect data about a controller class
+class ControllerInfo {
+    public ControllerInfo() {
+        ActionMethods = new HashSet<ActionMethodInfo>();
+        ViewsFolder = new ViewsFolderInfo();
+    }
+
+    public AreaInfo Area { get; set; }
+
+    public string AreaName {
+        get { return Area.Name ?? ""; }
+    }
+
+    public string T4MVCControllerFullName {
+        get {
+            string name = HelpersPrefix;
+            if (!String.IsNullOrEmpty(AreaName))
+                name += "." + EscapeID(Area.Namespace);
+            return name + "." + Name; ;
+        }
+    }
+
+    public string ViewPath {
+        get {
+            if (string.IsNullOrEmpty(Area.Name))
+                return String.Format("~/{0}/{1}/", ViewsRootFolder, Name);
+            else
+                return String.Format("~/{0}/{1}/{2}/", AreasFolder, ViewsRootFolder, Name);
+        }
+    }
+
+    // True when this is not a real controller, but a placeholder for views folders that don't match a controller
+    public bool NotRealController { get; set; }
+
+    public bool HasExplicitConstructor { get; set; }
+    public bool HasExplicitDefaultConstructor { get; set; }
+    public bool HasDefaultConstructor { get { return !HasExplicitConstructor || HasExplicitDefaultConstructor; } }
+    public bool IsAbstract { get; set; }
+
+    public bool GeneratedCodeIsUpToDate { get; set; }
+
+    public string ClassName { get; set; }
+    public string Name {
+        get {
+            // Trim the Controller suffix
+            return ClassName.Substring(0, ClassName.Length - ControllerSuffix.Length);
+        }
+    }
+
+    public string Namespace { get; set; }