Mocking Sitecore with Microsoft Fakes part 1

08/04/2013 12 comments

This post have been  in making for a while or since the Visual Studio 2012 Update 2 was announced. Especially since the update added Microsoft Fakes to VS 2012 Premium. With Fakes it is now possible to mock  Static methods and otherwise unmockable class’ for example DataTime.Now or a Sitecore Item. So now finally it is possible to do unit testing with Sitecore or if you like TDD. I know that this has been possible for a while if you used Glass Mapper og had access to a Typemock license.

This post is the first in a small series, on how to mock Sitecore out using Microsoft Fakes, and to begin with, I will have a look and extracting data from a Sitecore Item.

The two ways to get data from item i will cover here is either accessing a field through the field collection using item.Field[name of your field] or by the indexing found on the BaseItem which Item derives from like this item[name of your field].

The two code snippets below is used to run my test against one uses Field[] the other Item[]

 

public class CodeSnippet
{
   public static ID MY_TEXT_FIELD = new ID("{dc321650-5b1d-479a-ae81-c04d6585140d}");
   public static ID MY_CHECKBOX_FIELD = new ID("{a007c875-5192-4068-864d-523ffaa0b4ca}");
   public static string MISSING_TEXT = "MISSING TEXT";

   public string RunWithDataFromFieldCollection(Item item)
   {

      CheckboxField checkboxField = new CheckboxField(item.Fields[MY_CHECKBOX_FIELD]);
      if (checkboxField.Checked)
      {
        TextField textField = new TextField(item.Fields[MY_TEXT_FIELD]);
        string textValue = textField.Value;
        if (!string.IsNullOrEmpty(textValue))
          return textValue;
        return MISSING_TEXT;
      }
      return string.Empty;
  }

  public string RunWithDataFromBaseCustomItemIndex(Item item)
  {
    bool isChecked = item[MY_CHECKBOX_FIELD] == "1";
    if (isChecked)
    {
      string textValue = item[MY_TEXT_FIELD];
      if (!string.IsNullOrEmpty(textValue))
       return textValue;
      return MISSING_TEXT;
    }
    return string.Empty;
  }
}

First we look at the code that uses the Field[], This example is much like using the TestItem if used in earlier post found here, or at least the setup is. You need to setup a fieldcollection and ensure that the fields used exists in the collection.

 [TestClass]
 public class TestFieldAccess
 {
    private CodeSnippet _codeSnippet;

   [TestInitialize]
   public void Initialize()
   {
     _codeSnippet = new CodeSnippet();
   }

   [TestCleanup]
   public void CleanUp()
   {
     _codeSnippet = null;
   }

   [TestMethod]
   public void Run_WithCheckboxFieldNotChecked_ShouldReturnEmptyString()
   {
     using (ShimsContext.Create())
     {
       Field checkBoxField = new ShimField()
       {
         IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD,
         ValueGet = () => "0"
       };
       FieldCollection fieldCollection = new ShimFieldCollection()
       {
         ItemGetID = id => checkBoxField
       };

       Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection};
       string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub);

       Assert.AreSame(string.Empty, actualText);
    }
  }

  [TestMethod]
  public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnMissingText()
  {

   using (ShimsContext.Create())
   {
     Field checkBoxField = new ShimField()
     {
       IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD,
       ValueGet = () => "1"
     };
     Field textField = new ShimField()
     {
      IDGet = () => CodeSnippet.MY_TEXT_FIELD,
      ValueGet = () =>string.Empty
     };
     FieldCollection fieldCollection = new ShimFieldCollection()
     {
      ItemGetID = id => id==CodeSnippet.MY_CHECKBOX_FIELD ?checkBoxField : id == CodeSnippet.MY_TEXT_FIELD ? textField : null
     };

     Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection };
     string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub);

     Assert.AreSame(CodeSnippet.MISSING_TEXT, actualText);
    }
   }

  [TestMethod]
  public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnActualTextFromTextField()
  {
    using (ShimsContext.Create())
    {
     string expectedText = " Should return this string";
     Field checkBoxField = new ShimField()
     {
       IDGet = () => CodeSnippet.MY_CHECKBOX_FIELD,
       ValueGet = () => "1"
     };
     Field textField = new ShimField()
     {
       IDGet = () => CodeSnippet.MY_TEXT_FIELD,
       ValueGet = () => expectedText
     };
    FieldCollection fieldCollection = new ShimFieldCollection()
    {
      ItemGetID = id => id == CodeSnippet.MY_CHECKBOX_FIELD ? checkBoxField : id == CodeSnippet.MY_TEXT_FIELD ? textField : null
    };

    Item itemStub = new ShimItem() { FieldsGet = () => fieldCollection };

     string actualText = _codeSnippet.RunWithDataFromFieldCollection(itemStub);

     Assert.AreSame(expectedText, actualText);
    }
   }
 }

and the result off course 3 green bars with 100 % test coverage.

 mocksitecorepart1a

And yes there is a lot of setup.

Now lets look at the indexing, here it becomes a little tricky since the derived BaseItem public methods isn’t overwritten on a ShimItem there for we will have to fake calls to BaseItem instead of the Item but still we need to create a stub of the item with ShimItem.

 

 [TestClass]
 public class TestIndexAccess
 {
   private CodeSnippet _codeSnippet;

   [TestInitialize]
   public void Initialize()
   {
     _codeSnippet = new CodeSnippet();
   }

   [TestCleanup]
   public void CleanUp()
   {
    _codeSnippet = null;
   }

   [TestMethod]
   public void Run_WithCheckboxFieldNotChecked_ShouldReturnEmptyString()
   {
     using (ShimsContext.Create())
     {
      ShimBaseItem.AllInstances.ItemGetID = (item, id) => "0";
      Item itemStub = new ShimItem();
      string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub);
      Assert.AreSame(string.Empty, actualText);
     }
   }

  [TestMethod]
  public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnMissingText()
  {
     using (ShimsContext.Create())
     {
      ShimBaseItem.AllInstances.ItemGetID = (item, id) => id == CodeSnippet.MY_CHECKBOX_FIELD ? "1" : id == CodeSnippet.MY_TEXT_FIELD ? string.Empty : null;
      Item itemStub = new ShimItem();

      string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub);

      Assert.AreSame(CodeSnippet.MISSING_TEXT, actualText);
    }
  }

  [TestMethod]
  public void Run_WithCheckboxFieldCheckedButNotTextInTextField_ShouldReturnActualTextFromTextField()
  {
    using (ShimsContext.Create())
    {
       string expectedText = " Should return this string";
       ShimBaseItem.AllInstances.ItemGetID =
        (item, id) => id == CodeSnippet.MY_CHECKBOX_FIELD ? "1" : id == CodeSnippet.MY_TEXT_FIELD ? expectedText : null;

      Item itemStub = new ShimItem();

      string actualText = _codeSnippet.RunWithDataFromBaseCustomItemIndex(itemStub);

      Assert.AreSame(expectedText, actualText);
   }
 }

}
<pre>

 

And once again the result off course 3 green bars with 100 % test coverage.

mocksitecorepart1b

It might no be as smooth as Typemock mocking framework, and yes demands a bit more setup, at least at to begin with.

Sitecore MVC new Ninject Controller Factory – clean version

23/10/2012 2 comments

In this post we will have  revisit my last blog post, using Ninject with Sitecore MVC. In this new approach we will simplify the code to do the exactly same thing as in the last post LINK.
So what we want is to be able to inject Concrete implementation in the our Sitecore Controller.
We will do this by creating a ninjectc controller factory and forwarding  to the default Sitecore controller factory, so our implementation is used when Sitecore creates it’s controller. By doing it this way all standard Sitecore MVC functionality will still work, but we now have the possibility to inject our concrete implementation on creation time of the controllers.

First we need a Factory for creating the Ninject kernel


 public class NinjectKernelFactory
 {
   public IKernel Create()
   {
    return LoadAssembliesIntoKernel(new StandardKernel());
   }

   private IKernel LoadAssembliesIntoKernel(IKernel kernel)
   {
     foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
     {
      try
      {
        kernel.Load(assembly);
      }
      catch (Exception)
      {
         //Empty Catch used because ninject have problem
         //with loading some of the Sitecore MVC assemblies.
        // Method .ToString()
      }
    }
     return kernel;
   }
 }

With that in place we can create the the NinjectControllerFactory


 public class NinjectControllerFactory : DefaultControllerFactory
 {

  private IKernel _kernel;
  public NinjectControllerFactory(IKernel kernel)
  {
    _kernel = kernel;
  }

  public override void ReleaseController(IController controller)
  {
    _kernel.Release(controller);
  }

  protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  {
    return (IController)_kernel.Get(controllerType);
  }
 }

]

All that is left is now for binding it all together in a new InitilizeContollerFactory


 public class InitializeNinjectControllerFactory
 {
   public virtual void Process(PipelineArgs args)
   {
    SetControllerFactory(args);
   }

  protected virtual void SetControllerFactory(PipelineArgs args)
  {
    NinjectKernelFactory kernelFactory = new NinjectKernelFactory();
    NinjectControllerFactory ninjectControllerFactory = new NinjectControllerFactory(kernelFactory.Create());
    SitecoreControllerFactory sitecoreControllerFactory = new SitecoreControllerFactory(ninjectControllerFactory);
    ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
  }
 }

And off course we need to swap the originale InitlizeControllerFactory with our new one.

Sitecore default :

</pre>
<processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc"/>

Replaced with this:


<processor type="SitecoreNinjectModule.InitializeNinjectControllerFactory, SitecoreNinjectModule"/>

Now Lets try it our I have created a really simple example first an Item with a controller rendering on.

And the sourcode for the controller


public class StoryController : SitecoreController

{
 private ITestInterface _testInterface;
 public StoryController(ITestInterface testInterface)
 {
 _testInterface = testInterface;
 }

public ActionResult From()
 {

ViewBag.Froms = _testInterface.Get();
 return View();
 }
 }

And now to the View Code

And our RazerView


@model dynamic
 <h2>From vIew</h2>

@ViewBag.Froms

And a simple TestInterface and TestClass


public interface ITestInterface
 {
 string Get();
 }


public class TestClass : ITestInterface
 {
 public string Get()
 {
 return "Hallo From Test";
 }
 }

And the output of it all

There we are a much nicer and cleaner solution presented then I came up with in my last blog post and without breaking any Sitecore functionalit, and off course you can still unit test the controller.

Sitecore MVC Ninject controller

17/09/2012 7 comments

This is last post in the series of three. As promised in my last post we will inject a repository through the construct in the controller. To do this we will use the Ninject as our IOC container, This will be a very quick walkthrough.

Also we to make it a bit easier to find controller that needs to have class’ injected we will create a derived class from the controller class’,

    public class NinjectController : Controller
    {}

Some of the assmeblies in sitecore mvc solution gives and error when trying to reflect upon them so there are a couple of catch’s in the code to handle some minor bugs.

Next we will create our controller factory  wrapping the Sitecores standard factory. This new Ninject controller factory will for the controller that should be loaded is of type nijectcontroller instantiate the corresponding concrete type of the constructers parameters.. Also this class loads all the ninject binding. Maybe it’s not so nice that it has multiple responsibility but it is left out for an exercise to refactor the code so it conforms to SRP 🙂 .

public class NinjectInitializeControllerFacotry
{
  public virtual void Process(PipelineArgs args)
  {
    this.SetControllerFactory(args);
  }

  protected virtual void SetControllerFactory(PipelineArgs args)
  {
    NinjectControllerFactory controllerFactory = new     NinjectControllerFactory(ControllerBuilder.Current.GetControllerFactory());
    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
  }
}

and the controller factory it self

  public class NinjectControllerFactory : SitecoreControllerFactory
  {
    public NinjectControllerFactory(IControllerFactory innerFactory) : base(innerFactory)
    {
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
      Assert.ArgumentNotNull(requestContext, "requestContext");
      Assert.ArgumentNotNull(controllerName, "controllerName");
      try
      {
         IController controller = ICreateController(requestContext, controllerName);
         return controller;
      }
      catch (Exception exception)
      {
        if (!MvcSettings.DetailedErrorOnMissingController)
        {
          throw;
        }
        return new ErrorHandlingController(controllerName, exception);
     }
   }

   public override void ReleaseController(IController controller)
   {
    var disposable = controller as IDisposable;

    if (disposable != null)
    {
      disposable.Dispose();
    }
  }

  private IController ICreateController(RequestContext requestContext, string controllerName)
  {
    string fullControllerName = string.Format("{0}Controller", controllerName);
    Type ninjectController = FindSpecificNinjectController(fullControllerName);
    if (ninjectController != null)
    {
      return (IController)Kernel.Get(ninjectController);
    }
    return base.CreateController(requestContext, controllerName);
  }

  private Type FindSpecificNinjectController(string controllerName)
  {
    IEnumerable<Type> ninjectControllers = GetNinjectControllers();
    if(ninjectControllers.Any())
    {
     Type selectedNincjectController = ninjectControllers.FirstOrDefault(controller => controller.Name == controllerName);
     if (selectedNincjectController != null)
     return selectedNincjectController;
   }
   return null;
  }

 private IEnumerable<Type> GetNinjectControllers()
 {
    List<Type> types = new List<Type>();
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    types.AddRange(FindDerivedTypes(assembly, typeof(NinjectController)));
    return types;
 }
 private IEnumerable<Type> FindDerivedTypes(Assembly assembly, Type baseType)
 {
   try
   {
    return assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t));
   }
   catch (Exception)
   {
    return new List<Type>();
 }
 }

  private static IKernel _kernel;
  private IKernel Kernel
  {
   get
   {
    if (_kernel == null)
      CreateKernel();
     return _kernel;
    }
 }

protected void CreateKernel()
 {
   _kernel = new StandardKernel();
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
   {
    if (HasNinjectModules(assembly))
    _kernel.Load(assembly);
  }
 }

public static bool HasNinjectModules(Assembly assembly)
 {
   Type baseType = typeof (NinjectModule);
   try
   {
    return assembly.GetTypes().Any(t => baseType.IsAssignableFrom(t));
   }
  catch (Exception)
  {
    return false;
  }
  }
}

So all that is left is to overwrite the standard controller factory.


<initialize>
 <processor type="SitecorePresentation.SitecoreMVCRoutes,SitecorePresentation">
 <mvcIgnoreHomePage>false</mvcIgnoreHomePage>
 </processor>
 <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc"/>
 <processor type="SitecoreMVCNinjectExtension.NinjectInitializeControllerFacotry, SitecoreMVCNinjectExtension"/>
 <!--<processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc"/>-->
 <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc"/>
 </initialize>

Next we can rewrite the controller from the last episode to conform to the new ninjectcontroller


public class NinjectItemController : NinjectController
 {
 private readonly IChildItemRepository _childItemRepository;
 public NinjectItemController(IChildItemRepository childItemRepository)
 {
 _childItemRepository = childItemRepository;
 }

public JsonResult Index(string id)
 {
 return Json(CreateObject(_childItemRepository.Get(id)), JsonRequestBehavior.AllowGet);
 }

&nbsp;

private IEnumerable<object> CreateObject(IEnumerable<Item> items)
 {
 List<object> objectList = new List<object>();
 foreach (Item item in items)
 objectList.Add(new { item.Name });
 return objectList;
 }

}

The repositories from the last post looks the samen but are listed below.

</pre>
public interface IChildItemRepository
{
IEnumerable<Item> Get(string parentItemId);
}
<pre>
</pre>
public class ChildItemRepository : IChildItemRepository
{
public virtual IEnumerable<Item> Get(string parentItemId)
{
ID parentId;
if(ID.TryParse(parentItemId,out parentId))
return GetChildNodes(parentId);
return new List<Item>();
}

private IEnumerable<Item> GetChildNodes(ID parentId)
{
Item parentItem = Sitecore.Context.Database.GetItem(parentId);
return parentItem.GetChildren();
}
}

Now to ninject magic the ninject module that factory are loading bindings from.


public class ChildItemRepositoryNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IChildItemRepository>().To<ChildItemRepository>();

}
}

And of course now we can rewrite out unittest to test the new controller.


public class ItemControllerTest
 {
 [Test]
 public void ItemControllWithNoIdShouldReturnEmptyList()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);

 JsonResult result = controller.Index(string.Empty);
 Assert.That(result.Data,Is.Empty);
 }

[Test]
 public void ItemControllWithInvalideIDShouldReturnEmptyList()
 {

IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
 JsonResult result = controller.Index("invalidID");
 Assert.That(result.Data, Is.Empty);
 }

[Test]
 public void ItemControllWithValidIdShouldReturnJsonListWithItemNames()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 List<Item> childList = new List<Item>();

 childList.Add(new ItemStub("stub-a"));
 childList.Add(new ItemStub("stub-b"));
 childList.Add(new ItemStub("stub-c"));
 childList.Add(new ItemStub("stub-d"));

Guid itemGuid = Guid.NewGuid();
 childItemRepositoryStub.Get(itemGuid.ToString()).Returns(childList);
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
 JsonResult result = controller.Index(itemGuid.ToString());

List<object>resultData = (List<object>) result.Data; ;
 Assert.AreEqual(4,resultData.Count());
 Assert.AreEqual(resultData[0].ToString(),"{ Name = stub-a }");
 Assert.AreEqual(resultData[1].ToString(), "{ Name = stub-b }");
 Assert.AreEqual(resultData[2].ToString(), "{ Name = stub-c }");
 Assert.AreEqual(resultData[3].ToString(), "{ Name = stub-d }");
 }
 }

The import thing about the rewrite of the test are the ugly SET on the itemctroller is removed as the repository is passed in as contructor argument.The result from the test

As a last service the image below shows the file structure in visual studio .

Thats all, now we can create easy to test controllers using a repository pattern.

Unit testing our MVC Controller for Sitecore

10/07/2012 Leave a comment

Is this post and as promised, we will unit test the controller we created in the last post.
So we will rewrite the controller a tiny little bit, so we can inject a child item repository, ie. creating a stub for the repository. Since Sitecore as default doesn’t ship with ControllerFactory where it is possible use dependency inject we will rewrite the controller instead, so it has a default repository it uses when Sitecore is instantiating the controller, and one it uses when testing, or more precisely you can pass one in to Repository property. The updated controller code is listed below. In my next post we will extend Sitecore so we can inject class through the contructor with dependency Inject using Ninject.

public class ItemController : Controller
{
private IChildItemRepository _childItemRepository;

public JsonResult Index(string id)
{
return Json(CreateObject(ChildItemRepository.Get(id)), JsonRequestBehavior.AllowGet);
}
public IChildItemRepository ChildItemRepository
{
get { return _childItemRepository ?? (_childItemRepository = new ChildItemRepository()); }
set { _childItemRepository = value; }
}

private IEnumerable<object> CreateObject(IEnumerable<Item> items)
{
List<object> objectList = new List<object>();
foreach (Item item in items)
objectList.Add(new { item.Name });
return objectList;
}

}

Now the three test cases
1. No id supplied the controller should return an empty result.
2. Supplied  with an invalide Guid the controller should return an empty result.
3. Supplied with a valid guid, the controller should return a JSon result with  the item names for all child items placed under the node with specified Guid,

Now for the Unit test class, as a mocking framework I’ve used NSubstitute. I’ve used the ItemStub I did in the this post Unit testing with Sitecore Item.  The code for this test Item is also as a service listet below.

public class ItemStub : Item
{
public ItemStub(string itemName)
: base(ID.NewID, new ItemData(new ItemDefinition(ID.NewID, itemName, ID.NewID, ID.NewID),Sitecore.Globalization.Language.Invariant, new Sitecore.Data.Version(1), new FieldList()), new Database("dummy"))
{
}

}

[TestFixture]
 public class ItemControllerTest
 {
 [Test]
 public void ItemControllWithNoIdShouldReturnEmptyList()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 ItemController controller = new ItemController();
 controller.ChildItemRepository = childItemRepositoryStub;
 JsonResult result = controller.Index(string.Empty);
 Assert.That(result.Data,Is.Empty);
 }

[Test]
 public void ItemControllWithInvalideIDShouldReturnEmptyList()
 {

IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 ItemController controller = new ItemController();
 controller.ChildItemRepository = childItemRepositoryStub;
 JsonResult result = controller.Index("invalidID");
 Assert.That(result.Data, Is.Empty);
 }

[Test]
 public void ItemControllWithValidIdShouldReturnJsonListWithItemNames()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 List<Item> childList = new List<Item>();

childList.Add(new ItemStub("stub-a"));
 childList.Add(new ItemStub("stub-b"));
 childList.Add(new ItemStub("stub-c"));
 childList.Add(new ItemStub("stub-d"));

Guid itemGuid = Guid.NewGuid();
 childItemRepositoryStub.Get(itemGuid.ToString()).Returns(childList);
 ItemController controller = new ItemController();
 controller.ChildItemRepository = childItemRepositoryStub;
 JsonResult result = controller.Index(itemGuid.ToString());

List<object>resultData = (List<object>) result.Data; ;
 Assert.AreEqual(4,resultData.Count());
 Assert.AreEqual(resultData[0].ToString(),"{ Name = stub-a }");
 Assert.AreEqual(resultData[1].ToString(), "{ Name = stub-b }");
 Assert.AreEqual(resultData[2].ToString(), "{ Name = stub-c }");
 Assert.AreEqual(resultData[3].ToString(), "{ Name = stub-d }");
 }

And a screenshot of the test status.

I know that there probably exists better ways to test json results, but it is out of scope for this post, and yes if it should be “clean tests” each test should only contain one assert, but is left for as an exercise for you :).

In the next post we will extend Sitecore so that we can create Controllers through dependency injection, with Ninject as an IoC container. With this solution we don’t need the ugly Set in the Repository Property.

Sitecore MVC the first controller.

03/07/2012 1 comment

Sitecore 6.6 is now out, (as a technical preview), with the realse of this version Sitecore now offers full support for MVC. There have all ready been quite a few blog post how to user MVC with Sitecore and some of the new fetures offers a great deal of ease when developing with Sitecore.

So this is the first in a series of how to develop with Sitecore using MVC and some of the benefits you get for free using MVC.

If you haven’t allready read John West blog post on how to setup you Sitecore solution to use MVC you can read them here start with Part 1 and use part 3 to setup a visual studio solution .John also wrote some simple examples on how to use the MVC framework to return JSON serialized result from a controller you read his original post here. For a first post in this Sitecore MVC series we will revisit his example and create our own controller but with a different approach, and with the soon to be released .Net 4.5 Framework which include the new web-api this should be even easier, and given you even more flexibility for free and out of the box, we will look back at this example when the final release is out.

By now you should now have an up an running Sitecore 6.6 solution. My visual studio solution setup is shown below.

Now lets create our first controller Code listed below.


public class ItemController : Controller
 {

public JsonResult Index(string id)
 {
 return Json(CreateObject(ChildRepository.Get(parentItemId)), JsonRequestBehavior.AllowGet);
 }

private ChildItemRepository _childRepository;
 public virtual ChildItemRepository ChildRepository
 {
 get { return _childRepository ?? (_childRepository = new ChildItemRepository()); }

}

private IEnumerable<object> CreateObject(IEnumerable<Item> items)
 {
 foreach (Item item in items)
 yield return new {item.Name};
 }

}

With the controller in place you can change the route to newly created controller John in his example uses a Pipeline in Sitecore to achieve this i like this approache so much more the using the global.asax so we reuse his code for this with some minor alternations. The Id of the node we want to retrieve children for if not specified default to the id of the Home-node. I’ve done this because i don’t like all his assert for empty id, and if you don’t like you can just use the original approach with optional url parameter.You could if liked alsp default the index action. But instead of getting the id trough the url route data you should supply it as a argument to the action method. The key here is that parameter Id must be named the same as the input parameter in index method.


public class SitecoreMVCRoutes
 {

public virtual void Process(Sitecore.Pipelines.PipelineArgs args)
 {
 this.RegisterRoutes(RouteTable.Routes);
 }

public bool MvcIgnoreHomePage { get; set; }

protected virtual void RegisterRoutes(RouteCollection routes)
 {
 if (this.MvcIgnoreHomePage)
 {
 routes.IgnoreRoute(String.Empty);
 }

routes.MapRoute(
 "Default", // Route name
 "Item/{action}/{id}", // URL with parameters
 new { controller = "Item", action = "Index", id = "{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}" } // Parameter defaults with Id to home node
 );
 }
 }

Now we will implement the busyness layer that will supply the controller with data.

This is done through a ChildRepository the code is listed below.

</pre>
public class ChildItemRepository
 {
 public virtual IEnumerable<Item> Get(string parentItemId)
 {
 ID parentId;
 if(ID.TryParse(parentItemId,out parentId))
 return GetChildNodes(parentId);
return new List<Item>();
 }

private IEnumerable<Item> GetChildNodes(ID parentId)
 {
 Item parentItem = Sitecore.Context.Database.GetItem(parentId);
 return parentItem.GetChildren();
 }
 }
<pre>

Lets test the new controller

First with out supplying a id,

Now with a valid id different form the home-node.

And now with a invalid id.

There we go the code does the same as Johns but with out all the assert tests.

Categories: .Net, C#, MVC, Sitecore 6, Uncategorized Tags: , ,

Running Sitecore Field Editor from a command

21/05/2012 15 comments

When working with Sitecores page editor sometimes you need to edit Hidden fields for examples page metadata ie. “Page Title, Metakeywords, Metadescription”.  You could probably use the editframe control and if in pageedit render out the metadatacontroll through a placeholder. But a more elegant way could be by adding a button in editor Ribbon see screenshot below, and executing  the page editor with a limited set of fields, and is this post we would create a general command tha does exactly that.

To make the command as general as possible in would be tied to a settings item of the type
“/sitecore/templates/System/WebEdit/Field Editor Button”

Which contains fields for displaying

  • Header
  • Editor Icon
  • List with fields that should be shown in the editor separated with “|”

And even better you can use this where you are using the sc:editframe.

Now you can add a button of you liking, you could make a chunk here:

/sitecore/content/Applications/WebEdit/Ribbons/WebEdit/Page Editor/Metadata

Under the chunk add a button
/sitecore/content/Applications/WebEdit/Ribbons/WebEdit/Page Editor/Metadata/Edit

The click event
“command:executefieldeditor(path=/sitecore/content/Applications/WebEdit/Edit Frame Buttons/Metadata/EditMetadata)”

executes the command “command:executefieldeditor” specified in the commands.config file.

Mine looks like this

  <command name="command:executefieldeditor" type="PT.Framework.ExecuteFieldEditorCommand.ExecuteFiledEditor,PT.Framework.ExecuteFieldEditorCommand"/>

Now to the code, by deriving from the “FieldEditorCommand” i get the execution of the page editor for free all i have to do is specifying the correct parameters in the virtual method “GetOptions”  , note that the “PageEditFieldEditorOptions” is located in the Sitecore.Client.dll.

<
public class ExecuteFiledEditor : FieldEditorCommand
{
  protected override PageEditFieldEditorOptions GetOptions(ClientPipelineArgs args, NameValueCollection form)
  {
    EnsureContext(args);
    return new PageEditFieldEditorOptions(form, BuildListWithFieldsToShow()) { Title = SettingsItem[HEADER], Icon = SettingsItem[ICON] };
  }

  private void EnsureContext(ClientPipelineArgs args)
  {
    CurrentItem = Database.GetItem(ItemUri.Parse(args.Parameters[URIPARAMETER]));
    Assert.IsNotNull(CurrentItem, CURRENTITEMISNULL);
    SettingsItem = Client.CoreDatabase.GetItem(args.Parameters[PATHPARAMETER]);
    Assert.IsNotNull(SettingsItem, SETTINGSITEMISNULL);
  }
 private List<FieldDescriptor> BuildListWithFieldsToShow()
  {
    List<FieldDescriptor> fieldList = new List<FieldDescriptor>();
    ListString fieldString = new ListString(SettingsItem[FIELDNAME]);
    foreach (string field in new ListString(fieldString))
     if (CurrentItem.Fields[field] != null)
      fieldList.Add(new FieldDescriptor(CurrentItem, field));

   return fieldList;
  }

 private const string FIELDNAME = "Fields";
 private const string HEADER = "Header";
 private const string ICON = "Icon";

 private const string URIPARAMETER = "uri";
 private const string PATHPARAMETER = "path";
 private const string CURRENTITEMISNULL = "Current item is null";
 private const string SETTINGSITEMISNULL = "Settings item is null";

private Item CurrentItem { get; set; }
 private Item SettingsItem { get; set; }

}

And the final result:

Categories: C#, Sitecore 6 Tags: ,

Queries in Datasource location on Sitecore Layouts

15/05/2012 10 comments

The datasource location specified on layouts is limited to Sitecore paths.

But working with multisite solutions it is sometimes necessary to have some items stored locally under Site.

This could be done fairly easy hooking in to the Sitecore pipeline “getRenderingDatasource”, found in the web.config under
“/configuration/sitecore/pipelines/getRenderingDatasource”

Below I’ve inserted my own

“PT.Framework.Pipelines.GetDatasourceLocation,PT.Framework.Pipelines”

<getRenderingDatasource>
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceLocation, Sitecore.Kernel" />
 <processor type="PT.Framework.Pipelines.GetDatasourceLocation,PT.Framework.Pipelines" />
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.SetFallbackDatasourceLocations, Sitecore.Kernel" />
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceTemplate, Sitecore.Kernel" />
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.GetTemplatesForSelection, Sitecore.Kernel" />
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.CheckDialogState, Sitecore.Kernel" />
 <processor type="Sitecore.Pipelines.GetRenderingDatasource.GetDialogUrl, Sitecore.Kernel" />
 </getRenderingDatasource>

The code for this step is really simple:

</pre>
public void Process(GetRenderingDatasourceArgs args)
 {
 Assert.IsNotNull(args, "args");
 DatasourceLocation = args.RenderingItem["Datasource Location"];
 if(QueryInDataSourceLocation())
 ProcessQuery(args);
 }

private void ProcessQuery(GetRenderingDatasourceArgs args)
 {
 ContextItemPath = args.ContextItemPath;
 ContentDataBase = args.ContentDatabase;
 Item datasourceLocation = ResolveDatasourceRootFromQuery();
 if (datasourceLocation != null)
 args.DatasourceRoots.Add(datasourceLocation);
 }

private bool QueryInDataSourceLocation()
 {
 return DatasourceLocation.StartsWith(_query);
 }

private Item ResolveDatasourceRootFromQuery()
 {
 string query = DatasourceLocation.Replace(_query, ContextItemPath);
 return ContentDataBase.SelectSingleItem(query);
 }

private string DatasourceLocation { get; set; }
 private string ContextItemPath { get; set; }
 private Database ContentDataBase { get; set; }
 private const string _query = "query:.";
<pre>

Now you can write queries like “./ancestor-or-self::*[@@templatekey=’site’]/Spots”

Update – Multiple datasource locations

Since one might want to have multiple Datasource Roots i made a change to the code so you can separate queries with the “|” delimiter. Note the normal sitecore path is still working and handled by the
“Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceLocation”.

Now you can write

query:./ancestor-or-self::*[@@templatekey=’main section’]/QueryGlobal|/sitecore/content/GlobalSpots|query:./ancestor-or-self::*[@@templatekey=’site’]/Spots


public class GetDatasourceLocation
 {
 public void Process(GetRenderingDatasourceArgs args)
 {
 Assert.IsNotNull(args, "args");
 DatasourceLocation = args.RenderingItem["Datasource Location"];
 ContextItemPath = args.ContextItemPath;
 ContentDataBase = args.ContentDatabase;
 DatasourceRoots = args.DatasourceRoots;
 if(QueryInDataSourceLocation())
 ProcessQuerys(args);
 }

private void ProcessQuerys(GetRenderingDatasourceArgs args)
 {

ListString possibleQueries = new ListString(DatasourceLocation);
 foreach (string possibleQuery in possibleQueries)
 {
 if (possibleQuery.StartsWith(_query))
 ProcessQuery(possibleQuery);
 }

 }
 private bool QueryInDataSourceLocation()
 {
 return DatasourceLocation.Contains(_query);
 }

private void ProcessQuery(string query)
 {

 Item datasourceLocation = ResolveDatasourceRootFromQuery(query);
 if (datasourceLocation != null)
 DatasourceRoots.Add(datasourceLocation);
 }

private Item ResolveDatasourceRootFromQuery(string query)
 {
 string queryPath = query.Replace(_query, ContextItemPath);
 return ContentDataBase.SelectSingleItem(queryPath);
 }

private string DatasourceLocation { get; set; }
 private string ContextItemPath { get; set; }
 private Database ContentDataBase { get; set; }
 private List<Item> DatasourceRoots { get; set; }
 private const string _query = "query:.";

}

Categories: C#, Sitecore 6 Tags: ,

Unit testing Sitecore with Typemock Isolater.

27/04/2012 5 comments

In my last post I gave a stub example for a Sitecore Item. With the stub item you could test functionality for retrieving field data, which greatly limits the functionality you can test. So in this post I will have a look at the more advanced mocking framework Isolator from Typemock.  With the Isolator framework it is possible to intercept calls for methods that normally wouldn’t be possible, for example static methods. How may this help us? Since Sitecore hasn’t made it any easier to do unit testing with a great deal of static classes and methods alike, you will be able to mock or stub them out with the Isolator framework. The following is a simple example for testing functionality for Item.Children, ie. Sitecores Childlist.

Here we have a simple piece of code that, given a specific Template ID, adds an item to a list and returns the list.

public class SectionFactory
{
   public static IEnumerable<Section> CreateCollection(Item columnItem)
   {
      ID sectionTemplateId = new ID(Constants.Templates.Section);
      ChildList children = columnItem.Children;
      List<Item> sections = new List<Section>();
      foreach (Item child in children)
      {
         if (child.TemplateID == sectionTemplateId)
            sections.Add(new Section(child))

      }

     return sections;
 }
}

Since getting the children for a Item invokes more than a few static methods and classes it is impossible to stub the functionality using most mocking frameworks. But with the Isolator Framework it is possible.

[TestFixture]
 public class SectionFactoryTest
 {
 [Test]
 public void SectionFactoryNoChildrenReturnEmpyList()
 {
 Item stub = Isolate.Fake.Instance<Item>();
 ChildList childlist = Isolate.Fake.Instance<ChildList>();
 Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);
 IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);
 Assert.AreEqual(new List<Section>(), sectionList);
 }

[Test]
 public void SectionFactoryOneChildReturnEmpyList()
 {
 Item stub = Isolate.Fake.Instance<Item>();
 Item child = Isolate.Fake.Instance<Item>();
 ChildList childlist = Isolate.Fake.Instance<ChildList>();

ID sectionTemplate = new ID(Constants.Templates.Section);
 Isolate.WhenCalled(() => child.TemplateID).WillReturn(sectionTemplate);
 Isolate.WhenCalled(() => childlist).WillReturnCollectionValuesOf(new List<Item>(){child});
 Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);

ID link = new ID(Constants.Fields.Section.Links);
 Isolate.WhenCalled(() => LinkRepository.GetCollection(child, link)).WillReturn(null);

IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);
 Section section = sectionList.FirstOrDefault();
 Assert.That(section, Is.Not.Null);

 }

[Test]
 public void SectionFactoryOneValidSectionChildChildReturnEmpyList()
 {
 Item stub = Isolate.Fake.Instance<Item>();
 Item sectionChild = Isolate.Fake.Instance<Item>();
 Item child = Isolate.Fake.Instance<Item>();
 ChildList childlist = Isolate.Fake.Instance<ChildList>();

ID sectionTemplate = new ID(Constants.Templates.Section);
 Isolate.WhenCalled(() => sectionChild.TemplateID).WillReturn(sectionTemplate);
 Isolate.WhenCalled(() => child.TemplateID).WillReturn(null);
 Isolate.WhenCalled(() => childlist).WillReturnCollectionValuesOf(new List<Item>() { child, sectionChild });
 Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);

ID link = new ID(Constants.Fields.Section.Links);
 Isolate.WhenCalled(() => LinkRepository.GetCollection(child, link)).WillReturn(null);

IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);

 Assert.AreEqual(1,sectionList.Count());

}
 }

So with the above test we managed to get a 100% test coverage for the code.
And with the Isolator Framework we could even mock out the Sitecore.Context. The Isolater framework is must a have if you want to do unit testing with Sitecore.

Unit testing with Sitecore Item

07/02/2012 7 comments
This post is about how to do unit testing  with Sitecore. Off course this is not easily done, because Sitecore requires a “context” to work.  There are a couple of blogs out there , that explains how to setup either a custom test runner, where you can xecute tests through a webpage. or alternative setup an app_config file so you can work with Sitecore without a httpcontext. Links to both examples are given in bottom of this posts.
Both are really good, but I would categorize testing this way more as Integrations tests.  Since they both depend on external resources  such as  Sitecore Database or httpcontext, it would be more fair to  call them Integration tests. Not that you shouldn’t do integration test you SHOULD, but theese are more “heavy to run”. A unit is the smallest testable part of an application, and a unit test should not depend on the external things such as a database, file system or a like, and more they should run fast, and by a click of a button. I’ve recently read the book  Working Effectively with Legacy Code by Michael Feathers, and it got me thinking there got a be an alternative way to do unit testing with Sitecore.
Finally I’ve recently had a discussion with one of collegeues, the discussion was about how you could do unit test with Sitecore and what you would win by doing so. He claimed that test would be more complex then the code it test, and you wouldn’t gain anything from such tests. This inspired me to do some extensive testing. To prove him wrong I found some code he wrote for a Sitecore solution see below, the code is taken from a navigation component and finds the navigation title fom a item,  navigation title is only shown if the item if   “show in menu”  checkboxfield is checked. Keep in mind the that I picked this example because it is simple.
public class NavigationTitleFactory
 {
 public static string Create(Item item)
 {
 bool showInNavigation = item.GetCheckBoxField(new ID(Constants.Fields.Navigation.Show));

if (!showInNavigation)
 {
 return string.Empty;
 }

string navigationTitle = item.GetStringField(new ID(Constants.Fields.Navigation.Title));

 if (string.IsNullOrEmpty(navigationTitle))
 navigationTitle = item.Name;

 return navigationTitle;
 }
 }

So the first problem that you meet is that a Sitecore Item is difficult  to mock or stub out for test, it would have been really nice if it had  inherit from a interface or had some virtual methods you could override in  a test implementation of with a mocking framwork.
But it turns out you can actually instantiate a Sitecore Item all  you have to do is passing in a lot of nulls “W.E.W.L.C by  M.Feather page 121” see the result below.
public class TestItem : Item
 {
 public TestItem(FieldList fieldList,string itemName="dummy")
 : base(new ID(new Guid()), new ItemData(new ItemDefinition(new ID(new Guid()), itemName, new ID(new Guid()), new ID(new Guid())), Language.Invariant, new Sitecore.Data.Version(1), fieldList), new Database("web"))
 {

}
 }
 With this test item, writing test should now become a lot easier. The itemName is optional because offen you dont care about the name of your item, but for this show case we do.
If you like ,you could pass in addiational arguments to the test item like database name, language, or version. and would still work. The fieldlist arguments should contain the list of field values you gonna use in your test, the item fields can only be referenced with Sitecore id’s as you will see in the example below. Now back to code challenge from my colleague . I want to test the logic for the  navigation title factory liseted above. Here are the test cases.

  1. An Item with show in navigation not checked – it should return an empty string.
  2. An item with show in navigation checked but no navigation titel should return the Name of the Item.
  3. An item with show in navigation checked and with a navigation title, it should return the value of the navigation title.

The test code i listed below should give 100% test coverage of the code.

[TestFixture]
 public class NavigationTitleFactoryTest
 {
 [Test]
 public void CreateItemWithShowInMenuFalseShouldReturnEmptyString()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show),"0");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title),"NavigationTitle");
 Item stub = new TestItem(stubFieldList);

 string navigationTitle = NavigationTitleFactory.Create(stub);

 Assert.IsNullOrEmpty(navigationTitle);
 }

[Test]
 public void CreateItemWithShowInMenuTrueNoNavigationTitleShouldReturnItemName()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "");
 Item stub = new TestItem(stubFieldList,"myItemName");
 string navigationTitle = NavigationTitleFactory.Create(stub);

Assert.AreEqual("myItemName", navigationTitle);
 }

[Test]
 public void CreateItemWithShowInMenuTrueShouldReturnItemNavigationTitle()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "NavigationTitle");
 Item stub = new TestItem(stubFieldList);
 string navigationTitle = NavigationTitleFactory.Create(stub);

Assert.AreEqual("NavigationTitle",navigationTitle);
 }
 }
This is  a simple example of how to do some unit testing with sitecore. Off course there are some problems with this approach you can’t access more complex attributes and functions of the Sitecore item, for example Children, path, and  functions in general that needs to communicate with Sitecore. But you can test more complex logic that are based in field values alone.
But what did we win with these tests, lets look at it this way. First of i saved time going in to Sitecore creating items setting field values and publishing, agreed it doesn’t take that long but with test like this i can repeat verification of my code by a click of a button. Also Should the Navigation Title Factory really care about which items it get from sitecore ie  the database  pr which context it runs in ? And it would even be pretty simple to add fallback from navigation title to another field value instead of item name and test this new functionality and ensure that the old functionality still works as excepted. And this is precisely what you want from a unit tests.With a unit test you it should be easy to change/modify ,refactor or add functionality to existing code and easily verify that you didn’t brake anything.
I wouldn’t test pure datacontainers, Items that only holds data to be shown. I would prefer to test these with integrationtest.

Allstair Deneys blog on unit testing with
Sitecore using a custom test runnner can be found here.How to change config files to work with Sitecore without a “context” here. By Mike Edwards

Dropbox integration with Sitecore

16/11/2011 Leave a comment
In this post I will make a simple integration from dropbox into the sitecore media library. This is an early release or more a sneak peak at what I hope in time will be a feature rich sitecore open source project/module  “Dropbox integration with Sitecore” With visioning and two sync, and any request that might come in.
So consider this an Alpha-release, I’ve used this Dropbox Context we build in the last blog post “introduction to the dropbox api“. I’ve changend it a little bit so I can be used to make a static .aspx file where you can build the accestoken file. The changend Dropbox context is shown below
 public class DropboxContext
    {
        private OAuthToken _accessToken;

        private string _applicationKey;

        private string _applicationSecret;
        private string _accessTokenPath;

        private OAuthRestClient _apiRestClient;
        private OAuthRestClient _contentiRestClient;

        private string _version = "1.0";

        public DropboxContext(DropBoxSettings settings)
        {
            _applicationKey = settings.ApplicationKey;
            _applicationSecret = settings.ApplicationSecret;
            _accessTokenPath = settings.AccessTokenPath;
            SetupApiRestClient();
            SetupContentRestClient();
        }

        private void SetupApiRestClient()
        {
            _apiRestClient = new OAuthRestClient("https://api.dropbox.com");
            _apiRestClient.ConsumerKey = _applicationKey;
            _apiRestClient.ConsumerSecret = _applicationSecret;
            _apiRestClient.Version = _version;
        }

        private void SetupContentRestClient()
        {
            _contentiRestClient = new OAuthRestClient("https://api-content.dropbox.com");
            _contentiRestClient.ConsumerKey = _applicationKey;
            _contentiRestClient.ConsumerSecret = _applicationSecret;
            _contentiRestClient.Version = _version;
        }

        public OAuthToken GetRequestToken()
        {

            return new OAuthToken(_apiRestClient.GetRequestToken("1/oauth/request_token"));

        }

        public void AuthorizeToken(OAuthToken token)
        {
            Process.Start("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + token.Token);
        }

        public void SetAccesToken()
        {
            OAuthToken accesToken = GetAccesTokenFromStore();
            if (accesToken == null)
            {
                OAuthToken requestToken = GetRequestToken();
                AuthorizeToken(requestToken);
              //  accesToken = UpgradeRequsetTokenToAccesToken(requestToken);
            }

            _accessToken = accesToken;
        }

        public void UpgradeRequsetTokenToAccesToken(OAuthToken requestToken)
        {
            if (requestToken == null)
                return;

            string tokenString = _apiRestClient.GetAccesToken("1/oauth/access_token", requestToken);
            OAuthToken token = new OAuthToken(tokenString);
            StoreAccessToken(tokenString);

        }

        public void StoreAccessToken(string tokenString)
        {
            FileStream fs = File.Open(_accessTokenPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine(tokenString);
            sw.Close();
            fs.Close();

        }

        private OAuthToken GetAccesTokenFromStore()
        {
            OAuthToken token = null;
            string tokenString = TokenStringFromFile();
            if (!string.IsNullOrEmpty(tokenString))
                token = new OAuthToken(tokenString);

            return token;
        }

        private string TokenStringFromFile()
        {
            FileStream fs = File.Open(_accessTokenPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamReader sw = new StreamReader(fs);

            string tokenString = sw.ReadToEnd();
            sw.Close();
            fs.Close();
            return tokenString;
        }

        public AccountInfo AccountInfo()
        {
            var response = _apiRestClient.QueryServer<AccountInfo>("1/account/info", GetAccesTokenFromStore());
            return response;
        }

        public FileEntry GetMetadata(string path)
        {
            string requestPath = "1/metadata/dropbox";
            if (!String.IsNullOrEmpty(path))
                requestPath = String.Format("{0}{1}", requestPath, path.ToLower());
            var response = _apiRestClient.QueryServer<FileEntry>(requestPath, GetAccesTokenFromStore());
            return response;
        }

        public byte[] GetFile(string path)
        {
            var response = _contentiRestClient.GetStream("1/files/" + path, GetAccesTokenFromStore());
            return response;
        }
    }

Since this i an early release I’ve build this simple .aspx page that provides the user with a link to allow acces to their dropbox and writes the accesToken to a flat file, the file can we then copy to the sitecore data folder for later use.

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    Token : <%# Token.Token %> <br />
    Token Secret  <%# Token.TokenSecret %><br />

    <a href="https://www.dropbox.com/1/oauth/authorize?oauth_token=<%# Token.Token %>" target="_blank">Autherize Dropbox</a>

    <asp:Button ID="Button2" Text="Store Token" runat="server" onclick="Button2_Click" />
    </div>
    </form>
</body>
</html>
 public partial class AutherizeDropBox : System.Web.UI.Page
    {
        private OAuthToken token;
        private DropboxContext context;
        protected void Page_Load(object sender, EventArgs e)
        {
            context = new DropboxContext(SitecoreDropboxSettingsRepository.Get());

            if (!IsPostBack)
            {

                DataBind();
            }
        }

        protected void Page_PreRender(object sender, EventArgs e)
        {

        }

        protected  OAuthToken Token
        {
            get
            {
                if (ViewState["token"] != null)
                {
                    token = new OAuthToken();
                    token.Token = ViewState["token"].ToString();
                    token.TokenSecret = ViewState["tokenSecret"].ToString();
                }
                if (token == null)
                {
                    token = context.GetRequestToken();
                    ViewState["token"] = token.Token;
                    ViewState["tokenSecret"] = token.TokenSecret;
                }
                return token;
            }
        }

        protected void Button2_Click(object sender, EventArgs e)
        {

                OAuthToken t = Token;
                context.UpgradeRequsetTokenToAccesToken(t);

        }
    }

For dropbox context to work we need a valid dropboxcontext setting datacontainer. Information for this i stored as settings in the web.config

   <setting name="ApplicationKey" value="YOURAPPLIKATIONKEY" />
   <setting name="ApplicationSecret" value="YOURAPPLIKATIONSECRET" />
   <setting name="AccessTokenPath" value="PATHTOYOUFILETHATCONTAINSACCESSTOKEN" />

For this simple version of dropbox syncing I’ve build a simple sitecore task to run a “Download all service” from dropbox. It’s start by deleting all old the previously downloaded content from dropbox and the download from all content from dropbox root ie. “dropbox/”.

public class DropboxDownloadTask
    {
        CreateMediaItemService _createMediaItemService;
        public void Execute()
        {
            _createMediaItemService = new CreateMediaItemService();
            GetRoot();
        }

        private void GetRoot()
        {
            MetatadataRepository metatadataRepository = new MetatadataRepository();
            FileEntry root = metatadataRepository.Get();
            IterateOverChildren(root);
        }

        private void  IterateOverChildren(FileEntry folderEntry)
        {

            foreach (FileEntry entry in folderEntry.Contents)
            {
                if (entry.Is_Dir)
                  IterateOverChildren(GetFileEntry(entry));
                else
                  _createMediaItemService.CreateMediaItem(entry);
            }
        }

        private FileEntry GetFileEntry(FileEntry entry)
        {
            MetatadataRepository metatadataRepository = new MetatadataRepository();
            return metatadataRepository.Get(entry);
        }
    }

The task uses some file- and metadata-repositories that communicate with dropboxcontext and a simple settings repository.

 public static class SitecoreDropboxSettingsRepository
    {
        public static DropBoxSettings Get()
        {
            DropBoxSettings settings = new DropBoxSettings();
            settings.ApplicationKey = Settings.GetSetting("ApplicationKey");
            settings.ApplicationSecret = Settings.GetSetting("ApplicationSecret");
            string path = Settings.GetSetting("AccessTokenPath");
            settings.AccessTokenPath = HttpContext.Current.Server.MapPath(path);
            return settings;
        }

    }
public class MetatadataRepository
    {
        private DropboxContext _context;
        public MetatadataRepository()
        {

            _context = new DropboxContext(SitecoreDropboxSettingsRepository.Get());
        }

        public FileEntry Get()
        {
            return _context.GetMetadata(String.Empty);
        }

        public FileEntry Get(FileEntry entry)
        {
          return _context.GetMetadata(entry.Path);
        }
    }
 public class FileDataRepository
    {
        private  DropboxContext _context;
        public FileDataRepository()
        {

            _context = new DropboxContext(SitecoreDropboxSettingsRepository.Get());
        }

        public  byte[] Get(FileEntry entry)
        {
          return _context.GetFile(entry.Root+entry.Path);
        }
    }

Finally the task have a simple service that is responsible for adding a media stream to the sitecore media library.

 public class CreateMediaItemService
    {

        private MediaCreator _creator;
        private MediaCreatorOptions _mediaOptions;

        public void CreateMediaItem(FileEntry folderName)
        {
                 using (new SecurityDisabler())
                          AddStreamToMediaLibrary(folderName);

        }

        private void AddStreamToMediaLibrary(FileEntry folderName)
        {
             if(folderName.Parent == "dropbox")
                 SetMediaOptions(folderName.Parent, folderName.Name);
            else
                SetMediaOptions(String.Format("dropbox/{0}",folderName.Parent), folderName.Name);
            _creator = new MediaCreator();

            FileDataRepository fileDataRepository = new FileDataRepository();
            byte[] bytes = fileDataRepository.Get(folderName);

            MemoryStream theMemStream = new MemoryStream();

            theMemStream.Write(bytes, 0, bytes.Length);
            try
            {

                    _creator.CreateFromStream(theMemStream, folderName.Name, _mediaOptions);
            }
            catch (Exception e)
            {
                throw new Exception(folderName.Name, e);
            }
        }

        private void SetMediaOptions(string sitecorePath, string itemName)
        {
            _mediaOptions = new MediaCreatorOptions();
            _mediaOptions.FileBased = false;
            _mediaOptions.IncludeExtensionInItemName = false;
            _mediaOptions.KeepExisting = false;
            _mediaOptions.Versioned = false;
            _mediaOptions.Destination = String.Format("/sitecore/media library/{0}/{1}", sitecorePath, ItemUtil.ProposeValidItemName(itemName));
            _mediaOptions.Database = Database.GetDatabase("master");

        }

    }

Offcourse there are room for improvement and the code could use some cleaning up to is more “Clean Code” Uncle Bob.  You can download the source code for this project at Pentia. I’m more then interrested in hearing what features should be on the todo-list so let me know.

Categories: .Net, C#, Sitecore 6 Tags: , ,