Archive

Archive for the ‘C#’ Category

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 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: , ,

Introduction to the dropbox api version 1.0

02/11/2011 2 comments

In this post i will explore some of the basic functionality of the new Dropbox Api, you can read more about here https://www.dropbox.com/developers/reference/api. As you can see in the description you talk to dropbox using rest, and OAuth as authentication method. I could of course buil my own OAuth helper instead i will use this OAuth base utility found at https://www.dropbox.com/developers/reference/api  not that i that think it written as clean “clean code”  I like it does the job, but i will give some trouble writting clean crisp method of some of the classes in this post. As a restclient  I will use the client from  http://restsharp.org/.
Some of the code in this post is also done very naive with no error handling so of course if you want to use the code one should deffently address this issue.

Before you start coding anything you first need to go to dropbox and create an app
https://www.dropbox.com/developers/apps

The app key and app secret should be replaced where necessary  through out this post.

The authentication process at dropbox consist of three steps

  1. Get a request token
  2. Authorize the request token from step 1
  3. Upgrade the access token from step 1 to an Access token. The Access token should nt be stored and can be used in all future interaction with dropbox.

First a Token from dropbox i delivered from a rest response in form given below

oauth_token_secret=b9q1n5il4lcc&oauth_token=mh7an9dkrg59

So i made this simple yet  NOT VERY CLEAN CODE CLASS

public class OAuthToken
    {
        public OAuthToken()
        {
        }

        public OAuthToken(string tokenString)
        {
            string[] urlParts = tokenString.Split('&');
            string[] secretParts = urlParts[0].Split('=');
            string[] tokenParts = urlParts[1].Split('=');
            TokenSecret = secretParts[1];
            Token = tokenParts[1];
        }

        public string TokenSecret { get; set; }
        public string Token { get; set; }
    }

Most of the other restquest a response with Json and the restsharp can deserialize them in datacontainers.

Now with token class in place we can create the firs rest call to supply us with at instance of a request token.

We first need some dropbox OAuth settings stored in this datacontainer

public class DropBoxSettings
    {
        public string ApplicationKey { get; set; }
        public string ApplicationSecret { get; set; }
        public string AccessTokenPath { get; set; }
    }

With this we can create a new OAuthrestClient

public class OAuthRestClient
    {
        private OAuthBase _oAuth;
        private RestClient _restClient;

        public OAuthRestClient(string restHost)
        {
            _oAuth = new OAuthBase();
            _restClient = new RestClient(restHost);
        }

        public string GetRequestToken(string resource)
        {
            RestRequest request = AddOAuthParameters(resource);
            RestResponse response = _restClient.Execute(request);
            return response.Content;
        }

        public string GetAccesToken(string resource,OAuthToken token)
        {
            RestRequest request = AddOAuthParameters(resource,token);
            RestResponse response = _restClient.Execute(request);
            return response.Content;
        }

        public T QueryServer<T>(string resource, OAuthToken token) where T : new()
        {
            RestRequest request = AddOAuthParameters(resource, token);
            RestResponse<T> restResponse = _restClient.Execute<T>(request);
            return restResponse.Data;
        }

        public byte[] GetStream(string resource, OAuthToken token)
        {
            RestRequest request = AddOAuthParameters(resource, token,false);
            RestResponse restResponse = _restClient.Execute(request);
            return restResponse.RawBytes;
        }

        private RestRequest AddOAuthParameters(string resource, OAuthToken token=null,bool encodeSignatur=true)
        {
            RestRequest request = new RestRequest(Method.GET);
            string nonce = _oAuth.GenerateNonce();
            string timeStamp = _oAuth.GenerateTimeStamp();
            request.Resource = resource;

            request.AddParameter("oauth_consumer_key", ConsumerKey);
            request.AddParameter("oauth_nonce", nonce);
            request.AddParameter("oauth_signature_method", "HMAC-SHA1");
            request.AddParameter("oauth_timestamp", timeStamp);
            if(token != null)
                request.AddParameter("oauth_token", token.Token);
            request.AddParameter("oauth_version", "1.0");
            request.AddParameter("oauth_signature", BuildSignatureWithToken(resource, nonce, timeStamp, token, encodeSignatur));
            return request;
        }

        private string BuildSignatureWithToken(string resource, string nonce, string timeStamp, OAuthToken token,bool encodeSignature)
        {
            string normalizedUrl;
            string normalizedRequestParameters;
            string sig;
            if(token == null)
             sig = _oAuth.GenerateSignature(new Uri(string.Format("{0}/{1}", _restClient.BaseUrl, resource)), ConsumerKey, ConsumerSecret, "", "", "GET", timeStamp, nonce, out normalizedUrl, out normalizedRequestParameters);
            else
             sig = _oAuth.GenerateSignature(new Uri(string.Format("{0}/{1}", _restClient.BaseUrl,resource)),ConsumerKey, ConsumerSecret,token.Token, token.TokenSecret,"GET", timeStamp, nonce, out normalizedUrl, out normalizedRequestParameters);

            if(encodeSignature)
                sig = HttpUtility.UrlEncode(sig);
            return sig;
        }

        public string Version { get; set; }
        public string ConsumerKey { get; set; }
        public string ConsumerSecret { get; set; }
    }

There are som input parameters that are bool which i due some restriction bugs in the OAuth base where a token not should be added in the requesttoken call. And the dropbox api where the signature shouldn’t be urlencode when querying api-content.dropbox.com. We can now use this to get a request token.

  public OAuthToken GetRequestToken()
        {
            OAuthRestClient apiRestClient = new OAuthRestClient("https://api.dropbox.com");
            apiRestClient.ConsumerKey = YOUR APP KEY;
            apiRestClient.ConsumerSecret = YOUR APP SECRET;
            apiRestClient.Version = "1.0";
            return new OAuthToken(_apiRestClient.GetRequestToken("1/oauth/request_token"));
        }

Now with request token we can prompt the user for access to his or hers dropbox account this can for now only be done in a browser

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

Now with application authorized we ca upgrade the requesttoken to an accestoken, and store it in a secure place for now we store it as a string in a plaintext file.

 private OAuthToken UpgradeRequsetTokenToAccesToken(OAuthToken requestToken)
        {
 OAuthRestClient apiRestClient = new OAuthRestClient("https://api.dropbox.com");
            apiRestClient.ConsumerKey = YOUR APP KEY;
            apiRestClient.ConsumerSecret = YOUR APP SECRET;
            apiRestClient.Version = "1.0";
            string tokenString = apiRestClient.GetAccesToken("1/oauth/access_token", requestToken);
            OAuthToken token = new OAuthToken(tokenString);
            StoreAccessToken(tokenString);
            return token;
        }

        private 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;
        }

Now with an accestoken we can start talking to dropbox to the more fun stuff for example the userinformation

public class AccountInfo
    {
        public string Uid { get; set; }
        public string Display_Name { get; set; }
        public string Country { get; set; }
        public QuotaInfo Quota_Info { get; set; }
    }
 public class QuotaInfo
    {
        public string Shared { get; set; }
        public string Quota { get; set; }
        public string Normal { get; set; }
    }

or metadata for a a given path in the users dropbox

 public class FileEntry
    {
        public string Size { get; set; }
        public string Rev { get; set; }
        public string Thumb_Exists { get; set; }
        public string Bytes { get; set; }
        public string Modified { get; set; }
        public string Path { get; set; }
        public bool Is_Dir { get; set; }
        public string Icon { get; set; }
        public string Root { get; set; }
        public string Mime_Type { get; set; }

        public string Revision { get; set; }
        public List<FileEntry> Contents { get; set; }

        public string Name
        {
            get
            {

                if (String.IsNullOrEmpty(Path)) return String.Empty;
                if (Path == "/") return "dropbox";
                    return Path.Substring(Path.LastIndexOf("/") + 1);
            }
        }

        public string Parent
        {
            get
            {
                if (String.IsNullOrEmpty(Path)) return String.Empty;
                if (Path.LastIndexOf("/") == 0) return "dropbox";
                return Path.Substring(0,Path.LastIndexOf("/"));

            }
        }

    }
public AccountInfo AccountInfo()
        {
            OAuthRestClient apiRestClient = new OAuthRestClient("https://api.dropbox.com");
            apiRestClient.ConsumerKey = YOUR APP KEY;
            apiRestClient.ConsumerSecret = YOUR APP SECRET;
            apiRestClient.Version = "1.0";
            var response = apiRestClient.QueryServer<AccountInfo>("1/account/info", YOUR ACCES TOKEN);
            return response;
        }</pre>
&nbsp;
<pre>


Now we could can the byte[]  for a dropbox entry if it is not a folder, note when calling the api-content.dropbox.com the SIGNATURE PASSED TO DROPBOX SHOULD NOT BE URL -ENCRYPTED why i have no clue

public byte[] GetFile(string path)
        {
          OAuthRestClient apiRestClient = new OAuthRestClient("https://api-content.dropbox.com");
            apiRestClient.ConsumerKey = YOUR APP KEY;
            apiRestClient.ConsumerSecret = YOUR APP SECRET;
            apiRestClient.Version = "1.0";
            var response = apiRestclient.GetStream("1/files/dropbox/" + HttpUtility.UrlEncode(path), YOURRACCESTOKEN);
            return response;
        }

I’ve gathered all the functions in a simple Dropbox context.

So this wraps it up for now. in future post I will look into uploading files, but in the next post I will integrate this into the sitecore media library, for now only with download only and always overwrite. Coming soon so stay tuned.

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

Running Scheduledtasks with the jobmanager in Sitecore

06/10/2011 5 comments

I’ve recently had the need to execute a scheduled task. The simple way would be to fetch the scheduled task item from Sitecore an instantiate it as a ScheduledItem type found in Sitecore kernel namespace, and the Call the execute method found in the class.


Item item = database.GetItem(taskId);
 ScheduleItem scheduleItem = new ScheduleItem(item);
 scheduleItem.Execute();

This of course Will run the task in the context of the current user. This could lead to some minor issues where the user running task doesn’t have the same rights as the Sitecore build JobManager.The solution is to execute the task via the JobManager, which also is used by scheduler. This is done by providing simple JobOptions. The example belows instantiate a new MyTask and runs the execute method.

 JobOptions options = new JobOptions("Job Runner", "schedule", "scheduler", new MyTask(), "Execute", new object[] { scheduleItem.Items,  scheduleItem.CommandItem, scheduleItem });
 options.SiteName = "scheduler";
 JobManager.Start(options).WaitHandle.WaitOne();

The last parameter passed in as a new object is the parameter list passed on to the Execute Method.

 

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

Simple json deserialization in C#

06/06/2011 Leave a comment

In This post a brieflyguid to how to deserialize Json encoded strings. Json is string that can be used t describe complex datastruces fx. C# class’.

A simple Json string could look something like this.


{"TokenId":"tokenid", "TokenSecret":"secretcode"}

This corresponding C# class would look something like this


public class Token
{
public string TokenId { get; set; }
public string TokenSecret { get; set; }
}

     Now given the exaample Json string above we can deserialize using .Net stand javascript deserialze, like this. The Javascriptserializer is placed in System.web.Serializationn.  

   JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
Token deserializedToken = javaScriptSerializer.Deserialize<Token>("{\"TokenId\":\"tokenid\", \"TokenSecret\":\"secretcode\"}");

The "deserializedToken" object have now been intialized and both properties "TokenId" and "TokenSecret" has been set to the value folloing the ":" int json string. you can also have more complex structure represented by using in the Json string "[]" you will then have etend your class to contain a list as the example below


public class Token
{
public string TokenId { get; set; }
public string TokenSecret { get; set; }
public IEnumerable<Token> ChildTokens { get; set; }
}

So given the Json string


"{\"TokenId\":\"tokenid\", \"TokenSecret\":\"secretcode\" ,\"ChildTokens\" : [{\"TokenId\":\"tokenid\", \"TokenSecret\":\"secretcode\"}]}"

Your deserialized token object will contain a list with one  ChildToken.

Serialization is left for another post.

I will use this Json deserialization in a feature blogpost  to communicate with a OAuth server.

Categories: C#, Javascript Tags: ,

CMS Friendly URL With MVC (Simple)

04/05/2011 Leave a comment

I currently working on HUGE MVC project for a customer through Pentia. They are rebuilding their entire website from .Net webforms to MVC 3 with Razer.

So one of the first task was to build som simpel functionality tha could resolve their url delivered from the underlying CMS.Instead of register specific routes for the different pagetypes in the CMS

I came up with this solution shown in this blogpost.

To begin with I registered a new “CMS route in global.asax”. All URL shoul hit this route, ofcourse you could make other routes before this.


//RouteMap for CMS content
routes.MapRoute("CMSRoute", "{*url*}").RouteHandler = new CmsRouteHandler();

Now to the simple CmsRouteHandler implementation it simply calls the CmsHttpHandler a relying on the handler to find a correct controller to desplaying the current request.

public class CmsRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CmsHttpHandler(requestContext);
}
}

Now the CMSHttpHandle which for this blogpost is simplyfied to always returning the
frontpage. This should offcourse by matching the url to a page i DB and displaying the
and resolve it to the correct controller.


public class CmsHttpHandler : IHttpHandler
{
private RequestContext RequestContext { get; set; }
private IControllerFactory ControllerFactory { get; set; }
private IController Controller { get; set; }

public CmsHttpHandler(RequestContext requestContext)
{
RequestContext = requestContext;
}

public void ProcessRequest(HttpContext context)
{
HttpContext = context;
InstantiateControllerFactory();
InstantiateController();
SetupRequestContext();
Controller.Execute(RequestContext);
}

private HttpContext HttpContext { get; set; }

private void InstantiateControllerFactory()
{
ControllerFactory = ControllerBuilder.Current.GetControllerFactory();
}

private void InstantiateController()
{
Controller = ControllerFactory.CreateController(RequestContext, Pagetype);
}

private void SetupRequestContext()
{
RequestContext.RouteData.Values.Add("controller", Pagetype);
//Always render Index action, all pagetype controllers must implement Index action
RequestContext.RouteData.Values.Add("action", "Index");

AppendQueryStrings();
}

private void AppendQueryStrings()
{
foreach (string name in HttpContext.Request.QueryString)
RequestContext.RouteData.Values.Add(name, HttpContext.Request.QueryString[name]);
}

private static string ResolvePagetypeFromUrl()
{


//Should return apgetype from db/URL
return Pagetype


}

private string _pageType;
private string Pagetype
{
get
{
//Resolve pagetype from DB fx
//For now we will just return the frontpage
/return string should match controller name
return "frontpage";
}
}

public bool IsReusable
{
get
{
throw new NotImplementedException();
}
}
}

Hope you find it usefull..

Categories: C# Tags: ,