Tuesday, December 11, 2012

Yeyy!! My first Pokki is published!

I already wrote about what is Pokki how useful it could be. I spent some time thinking, what Pokki can I built that will be useful in my everyday work. I often find myself searching for online tools that do some simple dev related tasks and I thought that it will be great idea to bundle all kind of such tools into one Pokki that stays on my taskbar and is always just a click away from me.

What’s included in the first version

For the first version and included three tools:
  • Regex tool – very simple regex form that checks for a match or eventually replaces a string into string. This one can be widely extended with all kind of operations, quick options, etc.
  • HTML encoder tool – as vtrifonov wrote about in his post, bloggers often needs a tool that escapes html characters. This one encodes and decodes html string.
  • Color converter tool – converter between RGB and Hex format. Again this tool can be greatly extended – adding pallets, color picker, etc.
 The review process was quite straight forward. You first need to submit your app - there is an automated check if the package is ok and you need to fill some information that is used in the app store. After a day I got an email that there some issues with the package - the icon was not in a good shape and also some resizing issues. After submiting again - everything was ok and after another day the Pokki was published and downloadable through the store. I also added one update that followed the same workflow and everything went smoothly.

The results

One of the good things about Pokki is that they give you out of the box analytics about the usage of your app. During the first two weeks, my first app has 542 active users. I was really surprised by this and this is really inspiring for me to continue to develop this Pokki further.

Next steps …

I have already planned some features for the next versions. Since, my audience is mainly developers, I think it will be good idea to give my users the chance to create their own tools into my Pokki. This will be great showcase for using the local storage of Pokki’s framework and Kendo’s tabstrip. I will also give you the chance to send your tool to me, so I can consider adding it to the product.
Some other plans that I have are for – personalization, adding more features and extending the current ones. As you might have noticed, I’m really not a good UI person and will need some front end and design help. If you want to get involved – drop a comment or check out the git repository: WebDevTools.
You can download and install this Pokki by following this link (works even if you don’t have the framework already): Download Web Development Tools Pokki All suggestions and thoughts are more than welcome so feel free to abuse the comment form :)

Monday, November 19, 2012

KendoUI and Pokki living in symbiosis

Today I’m writing for two products that I really like – KendoUI and Pokki. For those of you that are not familiar – KendoUI is a framework for building desktop applications using HTML5. KendoUI is a comprehensive HTML5 and JavaScript framework for web and mobile development, brought by Telerik. What I find really fascinating about these two products is that they work together really well, even though there are not tested for integration. The main reason is that both of them are based on open standards (HTML5) which is really awesome and shows that HTML5 is a game changer for all of us.

How Pokki works

imageThe idea behind Pokki is awesome – developers can build little nice applications that sits in the taskbar of the user and updates him from time to time what’s going on on your app. There are hundreds of applications already built that brings your favorite websites even closer – Facebook, Gmail, grooveshark, etc. Pokki are even trying to fill the gap that Windows 8 left by removing that start menu from desktop environment by bringing the most frequently used operations back to you.

To achieve this, those guys use webkit (which is the browser engine used by most of the modern browsers today) to display your application to the user. This basically means that you can do everything you have used to do in webpages and even more – Pokki gives you the ability to use a local storage, show notifications to the user, update your application to all users through their infrastructure – basically what every developer needs. To make use of all this – you just have to develop your Pokki (you can even use the Developer Tools that you know from Chrome) and submit it for approval to the team. It’s so easy that I needed less than a day to build the Pokki I will write about in a minute.

Why using KendoUI

KendoUI is a great productivity booster when it comes to HTML5 development – with KendoUI you have it all MVVM framework, UI components, themes and much more. I’m not part of the team in Telerik that develops it, but our web teams try to adopt KendoUI as much as possible. Last time, we used it in Telerik.Tv quite successfully. Powered by JQuery the learning curve almost doesn’t exist you can start using it right away.

Building my first Pokki

When building my first Pokki, I wanted to create something useful for our customers at Telerik or at least a proof of concept that it worth investing time in this framework. As a first iteration I built something really simple – Pokki that takes some information from your Telerik account and shows it to you. To achieve this, I used a web service that we already use in our sites – the main obstacle is that it requires the user to be authenticated in www.telerik.com.
Pokki solves this problem quite elegantly – you can use a Web sheet. Web sheet is a chromeless browser loaded in your pokki that navigates the user to a pointed by you url. You can navigate the user to a login screen and once he authenticates in the website, a proper cookies is created in your Pokki (after all, this is a webkit based browser) and from then I can call our web service to bring the information for the authenticated user. Once I get this information from the server, I use KendoUI to create a grid show the information to the user in a useful form.
In this first version, after authentication users will be able to see if they have unread messages, products added to their shopping cart, a list with all bookmarked pages and their Telerik avatar. Users will get a notification in the task bar, once they get a new message. If this proves to be successful, we can add information about products releases, shortcuts to different Telerik resources, etc.

Final touches

I still need some final touches from our front enders to create a nice user experience that conforms Pokki design guidelines and will push the application for approval. If you are curious, you can take a sneak peak of the app in github where the whole source is published. The application still needs a lot of polishing and you can hit a lot of sharp edges, but it is good enough if you need an example of how to get started.
Github project: https://github.com/etabakov/TelerikPublicPokki
kendo-logo                                                                                        pokki_logo

Tuesday, November 13, 2012

Visual Studio 2012 and JustCode shortcuts cheat sheet

I hate using the mouse! It distracts me from what I’m doing and slows me down. The best way to avoid using the mouse are keyboard shortcuts. Luckily, Visual Studio comes with lots of them and JustCode adds even more. I use many of them, but occasionally during a pair programming session I find myself asking my peer “How you did this?”.
That’s why I went through the entire list with shortcuts for Visual Studio and JustCode and made a list with those I find useful, but still don’t use. According to me, the best way to remember shortcut keys is to have them on my desktop and that’s why I made a wallpaper with them. I hope you find it useful, too.
Wallpaper 1280x1024

Monday, November 5, 2012

Moving blogs from Blogger to Sitefinity

Currently, I’m quite happy with blogger.com as a blogging platform. But I can imagine that at a given time I would like to move my blog to more sophisticated content managements system. My choice would be of course Sitefinity, since I’m quite familiar with it (and it’s .NET). One of the main problems then would be to move my existing content from one platform to another. That’s why, I decided it will be a nice idea to write a Sitefinity module that does this.
Luckily, Blogger.com gives you the option to export all the content in a single xml file (in atom publishing format). So, half of the work is already done, you just need to import this content to your Sitefinity project. Using Sitefinity Thunder, I created a new Sitefinity Module (called eTabakov.Sitefinity.Modules.BlogsImport) and setup a backend page with a new widget in it. The widget just takes a file from a users PC. Then I have an interface IBlogsMigrationProvider:

It simply takes a Stream and tries to migrate the content in it to a Blog in Sitefinity. This way we can easily add additional blog platforms (Wordpress for example). Right now, I have only one imeplementation of this interface BloggerMigrationProvder:

It currently migrates all the blog posts together with their comments and tags ( and create new tags, if such doesn’t exist). In order to extract the information from the xml file, I decided to use XmlSerializer – that’s why there is a folder in the module called DataObjects that has a class for every xml element from the document that I need:


The whole project is published on github.com, so you can download the source and use the module to migrate your Blogger.com account. You are more than welcome to contribute to the project or just take a look around. https://github.com/etabakov/etabakov.Sitefinity.Modules.BlogsImport
Any comments are more than welcome, too!

Thursday, November 1, 2012

Recovering files from SQL Management Studio

If you have spent a lot of hours writing a query and after that accidentally restart your PC or close the SQL Management Studio or a lightening strike you and you haven't saved your query - fear not! There is still a little chance that you work is not completely lost.

You can check in the directory that Management Studio keeps its temporary files:
%UserProfile%\Documents\SQL Server Management Studio\Backup Files\

Hopefully, you can find there a bunch of files named similar to "~AutoRecover.~vs6820.sql" and one of them might be your file. It's a long shot but still worth trying if you desperate ;)

Thanks to this thread for saving me: http://www.sqlservercentral.com/Forums/Topic500268-149-1.aspx


Tuesday, October 30, 2012

Adding microdata format to your blog posts with Sitefinity

Our team @Telerik is currently busy with the migration of blogs.telerik.com to the latest version of Sitefinity 5.x. This task comes with some challenges, one of which is the improvement of the markup rendered by Sitefinity - we would like to make it semantic using schema.org. I already showed how to decorate your breadcrumb control with microdata format. To achieve the same for blog posts is just a little more work.

Here is an example of how a blog post should look like according to schemata.org:
<body itemscope itemtype="http://schema.org/Blog">
<!-- Blog post -->
    <article itemprop="blogPost">
        <header>
     <h1 itemprop="headline">Headline</h1>  
         <time itemprop="datePublished" datetime="2012-08-08">Wednesday, August 08, 2012</time>
  by 
  <a href="#" itemprop="author">Just* Team</a> | <a href="">Comments 0</a>
 </header>

<p>Blog post body paragraph 1</p>

<p>Blog post body paragraph 2</p>

    <section>
 <h2>2 Comments</h2>
 <article class="comment" itemscope itemtype="http://schema.org/UserComments">
           <div>
  <span itemprop="creator">Dejan</span>
  <time itemprop="commentTime" datetime="2012-08-09">09 Aug 2012</time>
     </div>

     <p itemprop="commentText">Comment text 1</p>
 </article>

 <article class="comment" itemscope itemtype="http://schema.org/UserComments">
     <div>
  <span itemprop="creator">Dejan</span>
  <time itemprop="commentTime" datetime="2012-08-09">09 Aug 2012</time>
     </div>

 <p itemprop="commentText">Comment text 2</p>
   </article>

  </section>
 </article>
<!-- Blog post -->
</body>
Notice that we have to add some attributes to the body tag. One way to achieve this is to use a master page. This will eventually lead to having a lot of master pages with just a different body tag for every content type that you have in your website. Another way is to create a custom control that renders these attributes for you - again quite inconvenient as you have to remember to drag this control on every page you need semantic markup ( and nothing will alert you that you have forgotten somewhere). The approach we choose to take is to derive from the Details view class for blog posts and override the PreRender event handler:
public class DetailPostViewSemanticMarkup : DetailPostView
    {
        protected override void OnPreRender(System.EventArgs e)
        {
         base.OnPreRender(e);

            if (this.Page != null)
            {
                HtmlGenericControl ctrlBody = this.Page.FindControlRecursively("grid") as HtmlGenericControl;
                if (ctrlBody != null)
                {
                    ctrlBody.Attributes.Add("itemtype", @"http://schema.org/Blog");
                    ctrlBody.Attributes.Add("itemscope", "itemscope");
                }
                else
                {
                    throw new ConfigurationException("The configured master page doesn't have <body runat=\"server\", which is required by the selected detail template for blogs");
                }
            }
        }
    }
After that you need to make Sitefinity to use this template for your details pages:
Go to the Administration and navigate to Settings > Advanced > Blogs > Controls > BlogPostsFrontend > DetailBlogPostsFrontend and change the property called "ViewType" to the fully qualified type name of the new class you created (in my case "BlogsWebApp.Views.Blogs.DetailPostViewSemanticMarkup"). After saving this setting you should be able to see the desired attributes in the body tag of the details pages in your blogs.

Next, navigate Design > Widget Templates and find the templates that is used in your blogs detail pages, by default it is called: "Full blog post item" and it applies to Blog posts - single. Go to edit it and change it in the way that fits the above specification. Our template looks similar to:
<%@ Control Language="C#" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.ContentUI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit"
    Assembly="Telerik.Sitefinity" %>
<%@ Import Namespace="Telerik.Sitefinity" %>
<telerik:RadListView ID="SingleItemContainer" ItemPlaceholderID="ItemContainer" AllowPaging="False"
    runat="server" EnableEmbeddedSkins="false" EnableEmbeddedBaseStylesheet="false">
    <layouttemplate>
        <article itemprop="blogPost">
            <asp:PlaceHolder ID="ItemContainer" runat="server" />
        </article>
    </layouttemplate>
    <itemtemplate>
        <header>
          <h1 itemprop="headline" class="mb20">
            <%# Eval("Title")%>
          </h1>
        </header>
        <div class="sfpostAuthorAndDate cuArticleInfo mb20">
            <time itemprop="datePublished" datetime='<%#((DateTime)Eval("PublicationDate")).ToString("yyyy-MM-dd")%>'>
               <%# ((DateTime)Eval("PublicationDate")).ToString("dddd, MMMM dd, yyyy")%>
            </time>

            <asp:Literal ID="Literal2" Text="<%$ Resources:Labels, By %>" runat="server" /> 
            <span itemprop="author">  
              <sf:PersonProfileView runat="server" /> 
            </span>
          |  <a href="#comments" class="comments">Comments <span class="bubble"><span class="point"></span><span class="bubble-icon rounded3">000</span></span></a>
        </div>
        <sf:ContentBrowseAndEditToolbar ID="BrowseAndEditToolbar" runat="server" Mode="Edit,Delete,Unpublish"></sf:ContentBrowseAndEditToolbar>
        <sf:FieldListView ID="PostContent" runat="server" 
            Text="{0}" Properties="Content" 
            WrapperTagName="div" WrapperTagCssClass="sfpostContent tPostContent mb40"
        />

        <asp:PlaceHolder ID="socialOptionsContainer" runat="server">
  
    </asp:PlaceHolder>
        <sf:ContentView 
             id="commentsListView" 
             ControlDefinitionName="BlogsCommentsFrontend"
             DetailViewName="CommentsMasterView" 
             ContentViewDisplayMode="Master"
             LayoutTemplatePath="~/ExternalTmpl/Comments/CommentsMasterView.ascx"
             runat="server" />
        <sf:ContentView 
             id="commentsDetailsView" 
             ControlDefinitionName="BlogsCommentsFrontend" 
             DetailViewName="CommentsDetailsView"
             ContentViewDisplayMode="Detail"
            LayoutTemplatePath="~/ExternalTmpl/Comments/CommentsDetailsView.ascx"
             runat="server" />
    </itemtemplate>
</telerik:RadListView>

Finally, we have to take care for the comments. Mind that we use external template for them in order to suit it for our needs:
<%@ Control Language="C#" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.ContentUI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>

<telerik:RadListView ID="commentsList" ItemPlaceholderID="ItemsContainer" runat="server" EnableEmbeddedSkins="false" EnableEmbeddedBaseStylesheet="false">
    <LayoutTemplate>
        <section>
            <h2 id="comments" class="sfcommentsTitle">
                <asp:Literal ID="comments" runat="server" />
            </h2>
            <div class="tCommentListing">
                <asp:PlaceHolder ID="ItemsContainer" runat="server" />
            </div>
        </section>
    </LayoutTemplate>
    <ItemTemplate>
        <article itemscope itemtype="http://schema.org/UserComments" class="sfcommentDetails">
            <div class="tCommentAuthor">
                <sf:SitefinityHyperLink ID="website" Text='<%# Eval("AuthorName") %>' NavigateUrl='<%# Eval("Website") %>' runat="server" Target="_blank" />
                <span itemprop="creator" id="authorName" runat="server">
                    <%# Eval("AuthorName") %>
                </span>
                <time class="tCommentDate" itemprop="commentTime" datetime="<%#((DateTime)Eval("DateCreated")).ToLocalTime().ToString("dd MMM") %>">
                    <sf:FieldListView ID="dateCreated" runat="server" Format="{DateCreated.ToLocal():dd MMM}" />
                </time>
            </div>
            <div class="tCommentText" itemprop="commentText">
                <asp:Literal ID="content" Text='<%# Eval("Content") %>' runat="server" />
            </div>
        </article>
    </ItemTemplate>
</telerik:RadListView>

That's all you need. One tiny remark: I couldn't find a way to add itemscope attribute only to the body tag without adding value for it. From what I found in the internet, it's perfectly ok to replace itemscope with itemscope="itemscope" and that what I did. I hope you find this helpful.

Monday, October 22, 2012

The making of DevReach.com - Part 4, Integration with PayPal Checkout Express

This is going to be a series of blog posts, that will review the process of making the latest version of DevReach.Com. For the this version we are going to use Sitefinity latest version and exploit all of its features as much as we can. We are currently working together with Sitefinity Team and producing a lot of feedback for them, part of the features that we need will probably be part of the next release of the product.

In the previous post from this series, I stopped at the final step of DevReach's process - choosing the payment method. I showed you how to place an offline order and promised to show you how we integrated our eCommerce module with PayPal Express Checkout.

First a few words what actually is PayPal Express Checkout and why we decided to take this approach. Sitefinity eCommerce module gives you out of the box integration with paypal using PayPal Payflow Pro. With this approach all the billing details (credit card details) are entered on your site and sitefinity process the payment using paypal API. On the other hand, when using PayPal Express Checkout - the users use their paypal account and all the sensitive data are entered on PayPal. We believe that this way our customer will be more comfortable, although we should probably consider pure credit card payments (as we get many requests this year).

Before starting

In order to start the steps bellow, you need to have a developer paypal account created (and a real one once you decide to go live). Creating dev account on paypal is easy: just go to developer.paypal.com and register. After that you will be able to create all kind of test accounts (both business and personal ones) that will be available in your paypal sandbox. You need at least on business test account in order to use its parameters for sending test payments to PayPal.

How PayPal Express Checkout works?

When the user selects the paypal method and click on the Place Order button, after saving all the information for the order, we navigate the user to PayPal page with some parameters that identify the transaction and some details for the transaction iteself (price, products, currency, etc.). Once the user confirms the order on the paypal website, he is navigated back to a certain page of your website and you need to confirm the payment on your side in order to complete the transaction. The response from the transaction is retrieved real time and the order can be automatically marked as paid.

The setup

In order to implement the PayPal Express Checkout widget, you should create a new Payment processor and Payment method. This way all the settings required for the widget will be fully configurable through the sitefinity administration real time. Of course, you can always simplify things and hard code those settings or extract them as a custom configuration in the web.config, but this way changing settings (especially when migrating from test environment to live environment) will be much harder.

Payment processor

First, you need a new class that derives from IPaymentProcessorProvider and implements all public methods of this interface. The implementations could be simply return null or even better - throw exception. These methods shouldn't be actually called as we are going to process the transaction in a bit different way that this processors can't handle. Then you should register you new payment processor in the Sitefinity administration (the actual types may vary according to your implementation):

You will also need a settings class that defines that layout of your paypal settings that will be shown in the backend. Of course, you can arrange it as you wish, but here is an example provided by eCommerce Team in Sitefinity that we used:
In sitefinity backend go to Administration -> Settings -> Advanced -> PaymentProcessor -> PaymentProcessorProviders

Click on the "Create New" button.
Set Id = f1aab7d9-71e4-4e4c-9359-7f78248147e5
Set Name = "PayPalExpress"
Set Title = "PayPal Express"
Set SettingsType = "Telerik.Sitefinity.Samples.Ecommerce.PayPalExpress.PayPalExpressSettings"
Set ViewProviderType = "Telerik.Sitefinity.Samples.Ecommerce.PayPalExpress.PayPalExpressSettingsField"
Set ProviderType = "Telerik.Sitefinity.Samples.Ecommerce.PayPalExpress.PayPalExpressProvider"

Payment method

After you have a payment processor ready, you have to register your new payment method (provided that you already have a paypal account, at least in the paypal sandbox). 

Go to Ecommerce -> Payment Methods
Click on Create a payment method
Set Name = "PayPal Express Checkout"
Select Type = "Online payment"
Select Payment processor = "PayPal Express"
Leave InvoiceUrl blank
Set CancelUrl to the name of your shopping cart page (do not end with ".aspx", just use the NAME of the page)
Set ReturnUrl to the name of your PayPal Review Order page
Set HostUrl to "https://www.sandbox.paypal.com"
Set ApiUrl to "https://api-3t.sandbox.paypal.com/nvp"
Set ApiUsername to "<your paypal test business account>"
Set ApiPassword to "<your paypal test password>"
Set ApiSignature to  "<your paypal test signature>"
Set ApiEnvironment to "sandbox"

And you are good to go with the further implementation. The standard approach would be to create a single widget that handles all PayPal express checkout interaction using multiple views. Unfortunately, our wireframes didn't allow us to do the same and we had to split some things across multiple pages and that's why we have to separate widgets.

In the last post, I stopped on the choose payment method screen and showed you how to place an offline order in the eCommerce module. Now, we will start where we ended last time and will continue with the placing of the online orders. Currently, we have only one online payment type (that is PayPal):


private bool PlacePayPalExpressOrder(PaymentMethod paymentMethod)
{
    PayPalExpressSettings paypalExpressSettings = PayPalExpressHelper.GetPaymentProcessorSettings(paymentMethod);
    string defaultCurrency = Config.Get<ecommerceconfig>().DefaultCurrency;

    CartOrder cartOrder = ShoppingCartManager.GetShoppingCartForUser();
    string providerName = ordersManager.Provider.Name;
    CheckoutState checkoutState = cartOrder.GetCheckoutState();
    Order order = ShoppingCartHelper.CopyCartToOrder(ordersManager, cartOrder, null, this.paymentPayPalProcessorName, providerName);
    User customerUser = null;
    Customer customer = ShoppingCartHelper.GetCustomerInfoOrCreateOneIfDoesntExsist(UserProfileManager.GetManager(), ordersManager, checkoutState, out customerUser);
    order.Customer = customer;
    ordersManager.SaveChanges();
    SetExpressCheckoutRequestType request = PayPalExpressHelper.CreatePaymentRequest(paypalExpressSettings.ReturnUrl,
        paypalExpressSettings.CancelUrl, order.Id, order.OrderNumber, order.Total, defaultCurrency);

    // Populate the payment record with the order details
    request.SetExpressCheckoutRequestDetails.PaymentDetails = new PaymentDetailsType[1];
    request.SetExpressCheckoutRequestDetails.PaymentDetails[0] = PayPalExpressHelper.CreatePaymentDetail(
        order, PayPalExpressHelper.CurrencyCodeStringToType(defaultCurrency));

    // Submit the initial PayPal Express checkout request
    SetExpressCheckoutResponseType response = PayPalExpressHelper.Checkout(paypalExpressSettings, request);

    if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
    {
        string token = response.Token;
        string host = paypalExpressSettings.HostUrl; // "www.sandbox.paypal.com"; 

        string redirectUrl = String.Format("{0}/cgi-bin/webscr?cmd=_express-checkout&token={1}", host, token);

        ShoppingCartManager.IncermentCartOrderStep();
        HttpContext.Current.Response.Redirect(redirectUrl, true);
        return true;
    }

    DisplayMessage(PayPalExpressHelper.GetResponseErrors(response.Errors));

    return false;
}
Basically what we do is to retrieve the settings from the backend, create and actual order in the eCommerce (and create a new Customer of such doesn't exist) and create and send payment request to PayPal. PayPal returns to us token (if everything with the request is ok) and we should redirect the user to paypal providing the token we had acquired.

public static SetExpressCheckoutRequestType CreatePaymentRequest(string returnUrl, string cancelUrl, Guid orderId,
                                                                    int orderNumber, decimal orderTotal, string currency)
{

    SetExpressCheckoutRequestType request = new SetExpressCheckoutRequestType();
    request.SetExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType();
    request.SetExpressCheckoutRequestDetails.CancelURL = cancelUrl;
    request.SetExpressCheckoutRequestDetails.ReturnURL = returnUrl;
    request.Version = "89.0";

    // ----- version 53.0 and above comment out this section
    request.SetExpressCheckoutRequestDetails.PaymentAction = PaymentActionCodeType.Authorization;
    request.SetExpressCheckoutRequestDetails.InvoiceID = orderNumber.ToString();
    request.SetExpressCheckoutRequestDetails.Custom =  orderId.ToString();
    request.SetExpressCheckoutRequestDetails.OrderDescription = "Order number " + orderNumber.ToString();
    request.SetExpressCheckoutRequestDetails.OrderTotal = new BasicAmountType()
    {
        Value = orderTotal.ToString("0.00"),
        currencyID = PayPalExpressHelper.CurrencyCodeStringToType(currency)
    };
    request.SetExpressCheckoutRequestDetails.GiftWrapAmount = new BasicAmountType()
    {
        Value = 0.ToString("0.00"),
        currencyID = PayPalExpressHelper.CurrencyCodeStringToType(currency)
    };
    // -----

    return request;
}
public static PaymentDetailsType CreatePaymentDetail(Order order, CurrencyCodeType currencyCode, int index = 0)
{
    decimal taxRate = order.Details.First().TaxRate;
    decimal itemTotal = order.Details.Sum(x => x.Total);

    decimal itemsTotalWithoutDiscounts = itemTotal;
    foreach (OrderDiscount od in order.Discounts)
    {
        if (od.DiscountAmountType == DiscountAmountType.Percent)
        {
            itemTotal = itemTotal - (itemTotal * od.DiscountAmount / 100);
        }
        else
        {
            itemTotal = itemTotal - od.DiscountAmount;
        }
    }

    decimal taxTotal = itemTotal * taxRate / 100;
    PaymentDetailsType paymentDetail = new PaymentDetailsType();
    paymentDetail.ItemTotal = new BasicAmountType() { Value = (itemTotal + taxTotal).ToString("0.00"), currencyID = currencyCode };
    paymentDetail.InsuranceTotal = new BasicAmountType() { Value = 0.ToString("0.00"), currencyID = currencyCode };
    paymentDetail.ShippingTotal = new BasicAmountType() { Value = order.ShippingTotal.ToString("0.00"), currencyID = currencyCode };
    paymentDetail.HandlingTotal = new BasicAmountType() { Value = 0.ToString("0.00"), currencyID = currencyCode };
    paymentDetail.TaxTotal = new BasicAmountType() { Value = 0.ToString("0.00"), currencyID = currencyCode };
    //paymentDetail.OrderTotal = new BasicAmountType() { Value = order.Total.ToString("0.00"), currencyID = currencyCode };
    //paymentDetail.OrderDescription = "Order number " + order.OrderNumber.ToString();
    paymentDetail.PaymentAction = PaymentActionCodeType.Authorization;

    int itemNumber = index + 1;
    int detailNumber = 0;
    paymentDetail.PaymentDetailsItem = new PaymentDetailsItemType[order.Details.Count + order.Discounts.Count];

    for (int i = 0; i < order.Details.Count; i++)
    {
        OrderDetail detail = order.Details[i];
        decimal productTotal = 0;
        if (detail.Quantity > 0)
        { 
            productTotal = detail.Total / detail.Quantity;  // Must send cost of each unit as Item amount
        }
        decimal productTax = productTotal * taxRate / 100;

        paymentDetail.PaymentDetailsItem[detailNumber] =
            new PaymentDetailsItemType()
            {
                Amount = new BasicAmountType() { Value = (productTotal + productTax).ToString("0.00"), currencyID = currencyCode },
                Description = detail.Title,
                Name = detail.Title,
                Number = detail.Sku,
                Quantity = detail.Quantity.ToString(),
                Tax = new BasicAmountType() { Value = 0.ToString("0.00"), currencyID = currencyCode },
                ItemWeight = new MeasureType() { unit = "LBS", Value = detail.Weight }
            };
        ++itemNumber;
        ++detailNumber;
    }

    decimal itemsTotal = itemsTotalWithoutDiscounts * (100 + taxRate) / 100;
    for (int i = 0; i < order.Discounts.Count; i++)
    {
        OrderDiscount discount = order.Discounts[i];
        decimal savingsAmount = -(itemsTotal * discount.DiscountAmount / 100);
        itemsTotal = itemsTotal + savingsAmount;

        paymentDetail.PaymentDetailsItem[detailNumber] =
            new PaymentDetailsItemType()
            {
                Amount = new BasicAmountType() { Value = savingsAmount.ToString("0.00"), currencyID = currencyCode },
                Description = discount.Title,
                Name = discount.Title,
                Quantity = "1"
            };
        ++itemNumber;
        ++detailNumber;
    }

    return paymentDetail;
}

public static SetExpressCheckoutResponseType Checkout(PayPalExpressSettings settings, SetExpressCheckoutRequestType request)
{
    CallerServices caller = new CallerServices();
    IAPIProfile profile = PayPalExpressHelper.CreateApiProfile(settings);
    caller.APIProfile = profile;

    SetExpressCheckoutResponseType response = new SetExpressCheckoutResponseType();
    response = (SetExpressCheckoutResponseType)caller.Call("SetExpressCheckout", request);

    return response;
}
After the user is redirected to paypal, he is supposed to enter his paypal credentials and to authorize the payment. Now, the process is pretty much our of your control. The order is save in the backend with status pending and you are able to check its details. Eventually, you can mark it as paid manually if for some reason we don't receive the payment through the standard workflow. In the most of the cases, after the customer has confirmed the payment, he will be redirected to a give url on your page (that you have provided in the request), where he should once again see an overview of his order and confirm it on your website as well. Have in mind that even though the user has accepted the transaction on paypal.com you still have no indication about this in your account and the user is able to drop the transaction at any time.

When the user is navigated to your website two additional parameters are provided through the request. Using them, you should check in paypal system whether such payment actually exists and if it's not paid already:


private void ReviewOrder()
{
    string payerId = "";
    string token = "";
            
    GetPayPalTokenAndPayerId(ref payerId, ref token);

    if (String.IsNullOrWhiteSpace(payerId) || String.IsNullOrWhiteSpace(token))
    {
        displayMessage("Unable to process order.");
        return;
    }
            
    GetExpressCheckoutDetailsResponseType details = PayPalExpressHelper.GetPaymentDetails(paypalExpressSettings, token);

    if (details.Ack == AckCodeType.Failure || details.Ack == AckCodeType.FailureWithWarning)
    {
        displayMessage(PayPalExpressHelper.GetResponseErrors(details.Errors));
        this.btnConfirmPayment.Visible = false;
    }
    else
    {
        this.btnConfirmPayment.Visible = true;
    }
    //displayOrderDetails(details);

    string orderId = new Guid(details.GetExpressCheckoutDetailsResponseDetails.PaymentDetails[0].Custom).ToString();
    this.btnConfirmPayment.CommandArgument = orderId;
}

private void GetPayPalTokenAndPayerId(ref string payerId, ref string token)
{
    payerId = "";
    token = "";

    if (HttpContext.Current.Request["payerId"] != null)
    {
        payerId = HttpContext.Current.Request["payerId"].ToString();
    }

    if (HttpContext.Current.Request["token"] != null)
    {
        token = HttpContext.Current.Request["token"].ToString();
    }
}
public static GetExpressCheckoutDetailsResponseType GetPaymentDetails(PayPalExpressSettings settings, string token)
{
    CallerServices caller = new CallerServices();
    IAPIProfile profile = CreateApiProfile(settings);
    caller.APIProfile = profile;

    GetExpressCheckoutDetailsRequestType request = new GetExpressCheckoutDetailsRequestType();
    request.Token = token;

    GetExpressCheckoutDetailsResponseType response =
    (GetExpressCheckoutDetailsResponseType)caller.Call("GetExpressCheckoutDetails", request);

    return response;
}
Notice that when initially sending request for payment to PayPal we provided unique id for this transaction in our system - in our case GUID but it could int or other type as well. This way, we have one-to-one relationship between the paypal token and our orders and we are able to retrieve the order from our database and show its details. After the user confirms the transaction we are ready to actually complete it:
void btnConfirmPayment_Command(object sender, System.Web.UI.WebControls.CommandEventArgs e)
{
    Guid orderId = new Guid(e.CommandArgument.ToString());
    if (ConfirmOrder(orderId))
    {
        EmailHelper.SendInvoiceEmail(orderId);
    }
}

private bool ConfirmOrder(Guid orderId)
{
    if (ConfirmPayment(orderId) == false)
    {
        return false;
    }

    try
    {             
        Order order = this.ordersManager.GetOrder(orderId);

        // Get existing customer record or create a new customer record
        Customer customer = ShoppingCartHelper.GetCustomerInfoOrCreateOneIfDoesntExsist(UserProfileManager, ordersManager, order);

        // Update the order with the customer and status
        order.Customer = customer;
        OrderStatus oldStatus = order.OrderStatus;
        order.OrderStatus = OrderStatus.Paid;
        this.ordersManager.SaveChanges();

        //raise here the status changed event handler directly
        ShoppingCartHelper.EcommerceEvents_OrderStatusChanged(order.Id, oldStatus, order.OrderStatus);

        PaymentResponse response = new PaymentResponse();
        response.IsSuccess = true;

        //OrderHelper.CapturePaymentResponse(order,         PayPalExpressHelper.PaymentProcessorName, response);

        ShoppingCartManager.CleanUp(orderId);

        OrderSuccess(orderId);
        return true;
    } // Try 
    catch (Exception ex)
    {
        Log.Write(ex, ConfigurationPolicy.ErrorLog);
        displayMessage(ex.Message);
        return false;
    }
}

private bool ConfirmPayment(Guid orderId)
{
    string payerId = "";
    string token = "";
    GetPayPalTokenAndPayerId(ref payerId, ref token);

    if (String.IsNullOrWhiteSpace(payerId) || String.IsNullOrWhiteSpace(token))
    {
        displayMessage("Unable to process order.");
        return false;
    }

    Order order = this.ordersManager.GetOrder(orderId);

    string defaultCurrency = Config.Get<ecommerceconfig>().DefaultCurrency;
    CurrencyCodeType currencyCodeType = PayPalExpressHelper.CurrencyCodeStringToType(defaultCurrency);

    DoExpressCheckoutPaymentResponseType response = PayPalExpressHelper.DoExpressCheckoutPayment(paypalExpressSettings, order.Total, currencyCodeType, token, payerId);

    if (response.Ack == AckCodeType.Success || response.Ack == AckCodeType.SuccessWithWarning)
    {
        return true;
    }
    else
    {
        displayMessage(PayPalExpressHelper.GetResponseErrors(response.Errors));
        return false;
    }
}

public static DoExpressCheckoutPaymentResponseType DoExpressCheckoutPayment(PayPalExpressSettings settings,
                                                                            decimal orderTotal, CurrencyCodeType currencyCode, string token, string payerId)
{
    DoExpressCheckoutPaymentRequestType request = new DoExpressCheckoutPaymentRequestType();
    request.DoExpressCheckoutPaymentRequestDetails = new DoExpressCheckoutPaymentRequestDetailsType();
    request.DoExpressCheckoutPaymentRequestDetails.Token = token;
    request.DoExpressCheckoutPaymentRequestDetails.PayerID = payerId;
    request.DoExpressCheckoutPaymentRequestDetails.PaymentAction = PaymentActionCodeType.Sale;
    request.DoExpressCheckoutPaymentRequestDetails.PaymentActionSpecified = true;

    request.DoExpressCheckoutPaymentRequestDetails.PaymentDetails = new PaymentDetailsType[1];
    request.DoExpressCheckoutPaymentRequestDetails.PaymentDetails[0] = new PaymentDetailsType();

    request.DoExpressCheckoutPaymentRequestDetails.PaymentDetails[0].OrderTotal =
    new BasicAmountType() { Value = orderTotal.ToString("0.00"), currencyID = currencyCode };

    CallerServices caller = new CallerServices();
    IAPIProfile profile = CreateApiProfile(settings);
    caller.APIProfile = profile;

    return (DoExpressCheckoutPaymentResponseType)caller.Call("DoExpressCheckoutPayment", request);
}

Ending

That's it. The code provided here is a little simplified, but should be able to get the basic idea. There are multiple ways to enhance and improve this scenarios and suit it to your needs. I'm not sure whether my explanations are sufficient enough, but do not hesitate to drop a comment and ask me, if you need further assistance.

Wednesday, October 17, 2012

Adding regions to your xml files in Visual Studio

Most of the website we maintain and develop at Telerik, needs the ability to redirects urls. Because of this, there are lots of legacy urls and the files that describe those redirects quickly get enormous (thousand of lines). Unfortunately, Visual Studio doesn't gives you the option to organize xml documents into regions as with the c# files.

Luckily, I stumbled upon a very useful extension for Visual Studio that does exactly this. It is presented as tool for organizing XAML files, but actually it deals with every xml document. I already tried it and it works great with both VS 2010 and VS 2012.

The syntax is pretty much the same as we know it:
<!-- #Region Some Useful Label here -->
//your xml document
<!-- #EndRegion -->

Check it out: http://inchoatethoughts.com/xamlregions or grab it from Visual Studio gallery. Big Thanks to the author Jacob Johnston!

Monday, October 15, 2012

The making of DevReach.com - Part 3, the eCommerce module

This is going to be a series of blog posts, that will review the process of making the latest version of DevReach.Com. For the this version we are going to use Sitefinity latest version and exploit all of its features as much as we can. We are currently working together with Sitefinity Team and producing a lot of feedback for them, part of the features that we need will probably be part of the next release of the product.

In the previous post, we created to new modules "Speakers" and "Lectures" with a relation between them. After that, we customized the front-end of these modules to fit our designs. Now, it's time to setup the eCommerce module and to build the registration process.

The registration process

For this edition of DevReach, together with our information architect and our event manager, we built entirely new registration process. We had main four steps, which were as follows:

1. Choosing how many passes of each type you wish and entering coupon code (if you have one).
2. Assigning the selected passes to people. This step was optional and this could be done after the registration is over as well.
3. Entering billing details - names, company information, invoice information, etc.
4. Choosing payment method.

Unfortunately, this workflow didn't fit very well in what Sitefinity eCommerce module offers out of the boxes. For, example step 2 is something that very little to do with the eCommerce module. Also, we wanted the coupon code to be entered before everything else in the shopping cart is done. The product list on our wire-frames was grouped by product type and sorted by a new field added by as - DisplayIndex. And many other tiny difference we had. Fortunately, the eCommerce module offers an API that basically gives you to do almost everything you wish using custom widgets and this is exactly what we did.

Step 1 - Choosing the products and entering the coupon code

This year we had two main products, which were basically the passes we offer - standard and vip. And we had some additional products to them, which were the various workshop that DevReach offered this for the first time. Every main product can be combined with every additional product (with some additional constraints). So, we decided to create two product types - conference pass and conference addon.
public IList<Product> GetProductsFromGivenType(ProductTypeEnum productTypeEnum)
{
      string productTypeTitle = productTypeEnum.ToProductTypeTitle();

      ProductType productType = this.catalogManager.GetProductTypes()
          .FirstOrDefault(pt => pt.Title == productTypeTitle);

      return catalogManager
           .GetProducts(productType.ClrType)
           .Where(p => p.IsActive)
           .OrderBy(p => p.FieldValue<decimal>("DisplayIndex"))
           .ToList();
}
This function takes an Enum as a parameter and retrieves that title of the product type and corresponds to this enum value. Before showing these products to the user, we have to get all active site-wide discounts and show the old and the new price. This is easy:
    public static IQueryable<Discount> GetDiscounts()
    {
        OrdersManager ordersmanager = OrdersManager.GetManager();
        return ordersmanager.GetDiscounts();
    }
And on item data bound for every product:
decimal newPrice = product.DisplayPrice;
foreach (Discount discount in SitewideDiscounts)
{
    Literal lRibbons = liHolder.FindControl("lRibbons") as Literal;
    lRibbons.Text += String.Format("<span class='ribbon'>{0}</span>", discount.Title);

    if (discount.DiscountAmountType == DiscountAmountType.Percent)
    {
        newPrice = newPrice - (newPrice * discount.DiscountAmount / 100);
    }
    else
    {
        newPrice = newPrice - discount.DiscountAmount;
    }
}

Label lnewPrice = e.Item.FindControl("lNewPrice") as Label;
lnewPrice.Text = String.Format("{0:0.##} EUR <span class='vat db '>including VAT*</span>", newPrice);

if (product.DisplayPrice > newPrice)
{
    Label loldPrice = e.Item.FindControl("loldPrice") as Label;
    loldPrice.Text = String.Format("{0:0.##} EUR", product.DisplayPrice);        }

On submit of this page, we have to gather all provided information and save it in the current order (or create new order if non exists).
public static CartOrder GetShoppingCartForUser(OrdersManager ordersManager)
{
    Guid shoppingCartId = GetShoppingCartId();
    CartOrder shoppingCart = ordersManager.TryGetCartOrder(shoppingCartId);
    if (shoppingCart == null)
    {
       shoppingCartId = Guid.NewGuid();
       RemoveShoppingCartCookie();
       SetShoppingCartId(shoppingCartId);
       shoppingCart = ordersManager.CreateCartOrder(shoppingCartId, null);
       ordersManager.SaveChanges();
    }

    return shoppingCart;
}

public static void RemoveShoppingCartCookie()
{
     HttpCookie httpCookie = new HttpCookie(EcommerceConstants.OrdersConstants.ShoppingCartIdCookieName, "");
     DateTime now = DateTime.Now;
     httpCookie.Expires = now.AddDays(-1);
     HttpContext.Current.Response.Cookies.Add(httpCookie);
}

Adding orderDetails for each product or updating the quantity if it already exists:
foreach (Product product in passes)
{
    int quantity = productQuantities[product.Id];
    CartDetail cartDetail = cartOrder.Details.SingleOrDefault(d => d.ProductId == product.Id);
    //if such product exists in the order
    if (cartDetail != null)
    {
        orderManager.SetQuantity(cartDetail, quantity);
    }
    else
    {
       OptionsDetails optionsDetails = new OptionsDetails();

       cartOrder.Currency = Config.Get<EcommerceConfig>().DefaultCurrency;
       orderManager.AddToCart(cartOrder, product, optionsDetails, quantity);
    }
}

And if a discount code is entered, we have to validate it:
var couponCodeValidator = new CouponCodeValidator(orderManager);
 if (couponCodeValidator.IsCouponCodeValid(couponName, cartOrder.SubTotalDisplay, out negativeMessage))
    {
          cartOrder.CouponCodes.Add(new CouponCode { Code = couponName });
    }
    else
    {
          if (oldCouponName != null)
          {
               cartOrder.CouponCodes.Add(new CouponCode { Code = oldCouponName });
          }
     }

Step 2 - Adding attendees to the order

For this step, we created a new module (using the module builder) called Attendees, which we use as a journal for attendees records. All attendees (names, email, pass type, etc.) for an order are kept here. Once the order is marked paid, from this data we create public users in the CMS and attach the users to the attendees. This way we have relationship between user and an order. We have created an hierarchy of objects for easier work with dynamic modules, but I will write about this some other time.

Step 3 - Billing details page

This page is probably the one that is most close to what the cms does. Nothing interesting to explain here:
void rbProceedPayment_Click(object sender, EventArgs e)
{
    if (rbInvoice.Checked)
    {
        if (!cbSameAsBilling.Checked)
        {
            Page.Validate("shippingAddress");
        }
        Page.Validate("billingAddress");

        if (rbCompanyInfo.Checked)
        {
            Page.Validate("companyInfo");
        }
        else
        {
            Page.Validate("personalInfo");
        }
    }

    if (Page.IsValid)
    {
        CartOrder cartOrder = ShoppingCartManager.GetShoppingCartForUser();
        cartOrder.Addresses.Clear();
        OrdersManager orderManager = new OrdersManager();
                    
        CartAddress billingAddress = orderManager.CreateCartAddress();
        billingAddress.FirstName = txtFirstName.Text;
        billingAddress.LastName = txtLastName.Text;
        billingAddress.Phone = txtPhone.Text;
        billingAddress.Email = txtEmail.Text;
        billingAddress.AddressType = AddressType.Billing;
        billingAddress.Address = "";
        billingAddress.City = "";
        billingAddress.Country = "";
        billingAddress.PostalCode = "";
        billingAddress.Company = "";

        if (rbInvoice.Checked)
        {
            billingAddress.Address = txtBillingStreet.Text;
            billingAddress.City = txtBillingCity.Text;
            billingAddress.Country = txtBillingCountry.Text;
            billingAddress.PostalCode = txtBillingPostalCode.Text;
            billingAddress.Company = txtCompanyName.Text;

            CartAddress shippingAddress = orderManager.CreateCartAddress();
            shippingAddress.FirstName = txtFirstName.Text;
            shippingAddress.LastName = txtLastName.Text;
            shippingAddress.Phone = txtPhone.Text;
            shippingAddress.Email = txtEmail.Text;
            shippingAddress.Company = txtCompanyName.Text;
            shippingAddress.AddressType = AddressType.Shipping;

            if (cbSameAsBilling.Checked)
            {
                shippingAddress.Address = txtBillingStreet.Text;
                shippingAddress.City = txtBillingCity.Text;
                shippingAddress.PostalCode = txtBillingPostalCode.Text;
                shippingAddress.Country = txtBillingCountry.Text;
            }
            else
            {
                shippingAddress.Address = txtShippingStreet.Text;
                shippingAddress.City = txtShippingCity.Text;
                shippingAddress.PostalCode = txtShippingPostalCode.Text;
                shippingAddress.Country = txtShippingCountry.Text;
            }

            cartOrder.Addresses.Add(shippingAddress);
        }

        cartOrder.Addresses.Add(billingAddress);
        orderManager.SaveChanges();

        Response.Redirect("~/register/checkout");
    }
}

Step 4 - Choosing the payment method

On this step we had to retrieve from the API all active payment methods and show them to the user:

                IQueryable<PaymentMethod> paymentMethods = ordersManager.GetPaymentMethods().Where(method => method.IsActive);

                rlvPaymentMethods.DataSource = paymentMethods;
                rlvPaymentMethods.DataBind();

The interesting part is when the Place Order button is clicked:
void btnPlaceOrder_Click(object sender, EventArgs e)
{
    Guid selectedPaymentMethodGuid = new Guid(Page.Request["paymentMethods"]);
    PaymentMethod paymentMethod = ordersManager.GetPaymentMethod(selectedPaymentMethodGuid);

    if (paymentMethod == null)
    {
        throw new ConfigurationErrorsException("the selected payment method do not exists");
    }

    if (!paymentMethod.IsActive)
    {
        throw new ConfigurationErrorsException("the selected payment method is not active");
    }

    if (paymentMethod.PaymentMethodType == PaymentMethodType.Offline)
    {
        PlaceOfflineOrder(paymentMethod);
        EmailHelper.SendInvoiceEmail(ShoppingCartManager.GetShoppingCartId());
    }
    else
    {
        switch (paymentMethod.Title)
        {
            case "PayPal Express Checkout":
                PlacePayPalExpressOrder(paymentMethod);
                break;
            default:
                throw new InvalidOperationException("Not implemented online payment method: " + paymentMethod.Title);
        }
    }

    this.RemoveShoppingCartCookie();
}

private void PlaceOfflineOrder(PaymentMethod paymentMethod)
{
    CartOrder cartOrder = ShoppingCartManager.GetShoppingCartForUser();
            
    ShoppingCartManager.CleanNotPurchasedProducts(cartOrder);
    CheckoutState checkoutState = cartOrder.GetCheckoutState();
    checkoutState.PaymentMethodId = paymentMethod.Id;
    checkoutState.PaymentMethodType = paymentMethod.PaymentMethodType;

    CartPayment payment = ShoppingCartHelper.GetCartPaymentFromCheckoutState(ordersManager, checkoutState);
    cartOrder.Payments.Add(payment);
    ordersManager.SaveChanges();

    ShoppingCartManager.PlaceOfflineOrder(checkoutState);

    Page.Response.Redirect("~/register/thank-you-offline", false);
}

public static bool PlaceOfflineOrder(CheckoutState checkoutState, Guid cartOrderId)
{
    UserProfileManager userProfileManager = UserProfileManager.GetManager();
    OrdersManager ordersManager = OrdersManager.GetManager();
    try
    {
        User customerUser = null;
        Customer customer = ShoppingCartHelper.GetCustomerInfoOrCreateOneIfDoesntExsist(userProfileManager, ordersManager, checkoutState, out customerUser);
                  
        OrderValidator orderValidator = new OrderValidator { IsOrderValid = true, StatusMessage = string.Empty };
  
        if (orderValidator.IsOrderValid)
        {
            IPaymentResponse paymentResponse = ordersManager.Checkout(cartOrderId, checkoutState, customer);

            // record the "success" state of the checkout
            checkoutState.IsPaymentSuccessful = paymentResponse.IsSuccess;

            Order order = ordersManager.GetOrder(cartOrderId);
            IncrementOrderNumber(order, ordersManager);

            // add the order to customer
            customer.Orders.Add(order);

            // Update the order
            order.Customer = customer;
            order.OrderStatus = checkoutState.PaymentMethodType == PaymentMethodType.Offline ? OrderStatus.Pending : OrderStatus.Paid;
            ordersManager.SaveChanges();

            // Send an email to the customer.
            try
            {
                this.SendOrderPlacedEmailToClientAndMerchant(order);
            }
            catch (Exception emailEx)
            {
                // Do nothing. If sending email fails, we still want CleanUp to occur and the user to be directed to the next page.
                Log.Write(emailEx, ConfigurationPolicy.ErrorLog);
            }

            CleanUp(cartOrderId);
        }
        else
        {
            //this.PaymentProblemPanel.Visible = true;
            //this.MessageControl.ShowNegativeMessage(orderValidator.StatusMessage);
        }

        return orderValidator.IsOrderValid;
    }
    catch (Exception ex)
    {
        Log.Write(ex, ConfigurationPolicy.ErrorLog);
        //this.PaymentProblemPanel.Visible = true;
        //this.MessageControl.ShowNegativeMessage(ex.Message);
        return false;
    }
}
  
This is all that is needed to place an offline order. I will write a separate post for the paypal integration we did together with the eCommerce team.

The last thing that worth mentioning is that in order to create the users, once the order is marked as paid - we needed a hook to subscribe for this event. Fortunately for us, the product team introduced a new event for changing the status of an order which was exactly what we needed, no matter if the order is online or offline:

protected void Application_Start(object sender, EventArgs e)
{
  Bootstrapper.Initialized += new     EventHandler<Telerik.Sitefinity.Data.ExecutedEventArgs>(Bootstrapper_Initialized);
}

private void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
{
  if (e.CommandName == "Bootstrapped")
  {
    EventHub.Subscribe<IEcommerceOrderStatusChangedEvent>(OnOrderStatusChanged);
  }
}

private void OnOrderStatusChanged(IEcommerceOrderStatusChangedEvent evt)
{
  var orderId = evt.OrderId;
  var oldOrderStatus = evt.OldOrderStatus;
  var newOrderStatus = evt.NewOrderStatus;
  if (newOrderStatus == OrderStatus.Paid)
  {
       if (!OrderHelper.IsOrderPaid(orderId))
       {
           AttendeeManager attendeeManager = new AttendeeManager();
           IList<Attendee> attendees = attendeeManager.GetAllPublishedByOrderId(orderId);
           UsersHelper.CreateCustomerUserAndSendActivationEmail(orderId, attendees);

           foreach (Attendee attendee in attendees)
           {
                UsersHelper.CreateUserAndSendActivationEmail(attendee, attendeeManager);
           }
       }
   }

   if (newOrderStatus == OrderStatus.Deleted)
   {
        OrderHelper.DeleteOrderInformation(orderId);
   }            
}

What's next?

Basically, this was our registration process. I skipped some of the code fore brevity and simplicity, but all the interaction with the eCommerce API is here. Next time I will show you the integration with paypal using the PayPal Express Checkout scenario. And stay tuned for the coming release of Sitefinity 5.2 - lots of new features and improvements will be released.

Wednesday, June 27, 2012

A .NET guy @Velocityconf 2012 - Day 2

So, the first day went great and the second one was even better. Actually, this was the first official day (not counting the workshops) and we get started with an awesome keynote.

The keynote

Jay Parikh from Facebook gave us a really inspiring speech about their company culture, work environment and of course some staggering numbers of their performance load. Something he said and I have already found to be very useful when getting new members into the team is the fast success. To give them the opportunity to commit a real peace of code in their first days in the new company is a big booster for what's coming next. I really enjoyed the short review of the Gatekeeper - the software Facebook wrote to manage to rollout of the new features. This kind of tools really make the difference between a just an ordinary software company which does what it needs for a living and an aggressive and innovative one - the automation is the key. Jay mentioned a lot of corporate values that we try to follow in Telerik, as well- point solutions, not problems; be honest; be a team player, not a player...

Two guys from Google Arvind Jain and Dominic Hamon talked about user experience and how we can improve it by preloading things. Something new for me was the:
<link rel="prerender" src="some_resource" />  for Chrome or
<link rel="prefetch" src="some_resource" /> for Firefox tag. Basically, this way you can advise the browser to prefetche some resources that you believe are more likely to be hit by the user. This is awesome and as you probably know, Google are using this technique for some time in their search engine and in the browser when typing for a url. What they do is to keep a track of what urls you choose with what probability when typing certain characters. This gives them the opportunity to preload amazingly accurate most of the resources you request. Just an example how effective this can be - Google has measured the seconds that they save for each user by preloading resources and it turned out that only for a day they save altogether 30 years of waiting to their users (for the search engine and the browser)! This is something we certainly want to use in our websites. 

We also got a very interesting talk from Richard Cook, who is not a programmer but has made a deep research on the topic "How complex systems fail". To sum up - statistics kills the details and if you want to really study and investigate a problem you should not observe the data from eagle eye, but get down to the single item level and study every single case individually. It looks like this has been the key to solving lots of mysteries in the human history and more specifically - that how the cause of the Cholera decease was found. By aggregating data you can even hide the problems and not now when you systems behave badly.

Performance implications of Responsive Web Design

Lately, we have been greatly using this technique. We just release a new version of TelerikTv (which I will blog about later) that is using a responsive layout to adapt to different screen parameters. What we should keep in mind when using responsive web design is that hiding or adapting content doesn't make it necessarily optimized for the particular device and you can even get a performance penalty when not implementing this technique in a proper way.

One thing to have in mind are the responsive images. Currently, it's very difficult to serve the proper image size for the specific device and layout. It looks like we need a new syntax in the in order to express different sources for our images. Otherwise, we can only adapt the visual content but the amount of data will still remain irrelevantly high. To do this today, you have several options neither of which are obvious winner and probably will be replaced in the future by a new standard introduced in HTML for example. Here is an interesting reading from A List Apart about this topic.

Roll back: the impossible dream

James Turnbull explained how hard is actually to make a real roll back of a system that is currently running and speculated on is this even possible in a system that is currently running. Something that worth mentioning that a lot of people count on their roll back procedures and have them as a possible last resort solution, but roll back routines are hardly ever tested and practised. Actually, performing an operation that one is doing for a first time when something terribly wrong has already happened and the pressure is enormous is probably not the best thing to do. May be we should not count on roll back procedures at all? May be we should invest time in actually solving the problem, instead of trying to rollback the systems that are constantly changing to their initial state without loosing the operational data.

As a side note, James mentioned about the myths that every company and team has about how to do certain things and how to don't do other. His advice was to go and re-think all the "We don't do things this way because, something terrible wrong happens every time we do it" statements we have used to use, find the cause and fix it. 

The expo hall

There are a big amount of companies presented at the expo area. Most of them, I haven't heard mainly because they are not targeted at the .NET world, but I found out that most of their tools can be useful in our environment. For example, a good impression I got for a company that offers performance dashboards as a service for you. By installing an agent that tracks the activities in your web processes on every live server you have, this agent collects data about windows performance counters for example and sends them to their service. There you get a nice presentation of your current live environment from all running servers. You can also add information from your logs, eCommerce solution and get all this data in one place. Something we should probably check out with our admin team.

Something I forget to mention yesterday was the webpagetest.org project. An online tool that makes a performance study on your website from different geo-locations with different browsers and gives you a nice analytical data about how your website behaves. Another interesting tool (still beta) that worth mentioning is http://httparchive.org/ - a nice source for statistical data regarding the http traffic worldwide. According to the trends shown there - Flash is steadily disappearing, websites are using more and more custom font faces for their representation, etc. Be sure not to miss it! 

Well, basically these were the highlights from today from VelocityConf. It has been a very busy day with lots of new information. I'm sure that tomorrow will be even better. 

Tuesday, June 26, 2012

A .NET guy @Velocityconf 2012 - Day 1

Thanks to my company, this year have the honour to attend VelocityConf - a conference about performance and optimizations organized by O'Reilly. Needless to say, that there are very few .net developers here beside me and my colleague - in fact we are still looking to find the first :). Still, the topics discussed here are general enough to apply to our developers that deal with web development regardless what technology and framework is used. This is my first conference outside Bulgaria and there are lots of everything stuff going on here in Santa Clara.

The first day was an optional one and consisted of four time slots with four different tracks. These had to be workshops, but I think almost all of them ended up like a regular sessions. Nevertheless, the topic and the speakers were very interesting, so I will try to present some useful abstract from those I attended.

The day started for me with the session "Understanding and Optimizing Web Performance Metric" by Bryan McQuade from Google. He explained in greater details how the browser's render works and showed us the most frequent problems we might get into it. He played with the PageSpeed Insights Tool and showed what usually slows the downloading, rendering and displaying of the typical web pages. We took a look at the Critical Path Explorer feature that will be of great help when optimizing the loading of your web pages. It now only shows you how many time each resources takes to load, but also reveals which resources is blocking the rest of the page, how much time it takes to apply the css for example to the web document and many other useful properties. Another great new thing I learned was the Navigation Timing API, which is basically a library that gives you information about how much time takes to get to different events while loading your web page. It starts with the DNS lookup, TCP connection establishing and ends with the completely loaded page. This API is part of a W3C specification and is implemented by most modern browsers - it's there to help you meassure the latency and find possible bottlenecks. My takeaways from this sessions are:
  • to be very careful with the document.write as this might greatly degrade the webpage rendering speed by blocking the processing of other resources. 
  • to be make redirects for mobile content cache-able per user as this will save some round-trips to the server (and round-trip to the server when browsing with mobile device can be quite expensive)
  • to include the complete certificate chain when using SSL in order to save additional requests
  • to specify the encoding of the resources in the response headers
The day continued with "Taming the mobile beast" by two guys from Google - Matt Welsh and Patrick Meenan. They presented a lot of tools for mobile development and more precisely performance measuring and optimization. The remote debugging feature of Chrome is something that worth mentioning. You can debug you web site by attaching your mobile device to you dev machine with USB cable. From there you can browse the website using your mobile connection (or WiFi one) and this way troubleshoot the real mobile experience (traffic is going through mobile carrier, rendered by real mobile browser, etc.). You can even inspect the DOM and you will get the selected element right on your device - really neat. In iOS 6 there will be similar feature for Safari. Those guys showed also numerous bookmarklets (bookmarklet is unobtrusive JavaScript stored as the URL of a bookmark in a web browser or as a hyperlink on a web page. "Wikipedia") like Firebug lite, ySlow mobile, jdrop (for sharing bookmarklets), dommonster (for inspecting the DOM), docsource, csses, snoopy, spriteme, navigation timing bookmarklet (that utilizes the mentioned above navigation timing API) and many others. Some things to have in mind when developing for mobile:
  • JavaScript is a lot slower when used in mobile browser (mainly because of the slower processors)
  • Mobile carriers use proxies that behave differently depending on the hardware and configuration used in the particular mobile operator. Testing with mobile connection is essential and WiFi cannot be a replacement.
  • Caching behaviour is different on different devices - in general it's much smaller, sometimes is not persisted after closing the browser or restarting the device.
  • Initiating a TCP connection is much slower operation, moreover it has different round-trip time in different countries
  • The LTE standard is getting popular and will get things better ... but not much, since the real bottleneck is not the speed, but the latency. It takes a several seconds for the devices only to negotiate for a radio channel with the cell.
  • Because of the above point - more parallel connections are not always coming for better. We get a performance penalty for negotiating for new connections.
After the lunch break Ian White from Neustar gave us the "Dev vs. Prod - Optimizing your site without making your build process suck). A great introduction in nginx - a lightweight server used mainly for serving static content. We have already discussed using this server for our resources with my colleagues and this session was a great demo of its features. Nginx proves to be a lot faster than IIS when comes to serving static content like images, stylesheets, etc. You get a great control on what is served to the client with what particular headers and properties. I hope that we will soon get the chance to put it into practice.

Last, I visited Baron Schwartch's session about "Benchmarks, Performance, Scalability and Capacity - What's behind the numbers?". Nice talk to gain general knowledge into the topic of preparing benchmarks and analysing charts and statistical data - not a big practical use for me, though. 

This great first day ended with a party by the pool area. I'm really excited and looking forward to day 2.

Monday, June 4, 2012

My impressions after developing with Visual Studio 2012 for a few weeks

Actually, I lied in the title. I first used Visual Studio 11 and now I switched to Visual Studio 2012 RC. For simplicity in this post, I will refer to all Visual Studio 11 betas with the new Visual Studio 2012 product name. I installed one of the first betas available on my work PC and tried to use it as a primary IDE. I will share with you, what I found useful and what still needs to be improved, according to me. I hope, my experience will help you start faster with the new environment and warn you for possible obstacles on your way.

The setup

The first thing that got my attention was that when you open a solution with the new Visual Studio there is a upgrade process like before. I had the impression that with this release Microsoft will allow us to work simultaneously with 2010 and 2012 and didn't expect to have upgrade process again. Nevertheless, all the project files are untouched, if you have to change them though with the new VS 2012 you will get some new elements in the configuration that won't prevent you from working with this project with VS 2010, which is really nice. Still, there were some changes to the solution file that made it unusable with  VS2010, so I made a copy of the solution file for working with the new Visual studio and left the old one untouched. This could be inconvenient if you frequently add projects or other solution items, since you will have to do this on both solutions, but happily we don't do this very frequently. (This might be subject to change in the next releases, I haven't check this in the Release Candidate. Still, having two solution files is not a big overhead, I think. EDIT: Thanks to Syd for updating me on this one, it looks like the issues I have been experiencing are no longer a problem in the latest RC.)

My very first thoughts when opened Visual Studio 2012

At first, I felt a little lost. Although, the menu structure and the different windows (Solution Explorer, Server explorer, etc.) are the same, you have to used to distinguishing buttons and icon not by color, but by shape. This was kind of frustrating in the beginning, but now after a few weeks I feel very comfortable with the new "metro" and "not-colorful" user interface. I almost immediately switched to the dark theme, which is really nice and the text highlighting is much better than my custom color theme I had in VS 2010. In the first versions, the background of some windows (Server Explorer for example) was white, which looked really bad in the dark theme, but I see now that this is resolved and the overall design when using the dark theme feels really native. 

Something else that amazed me - almost all my extensions that I used in VS2010 works perfectly OK with VS2012, from the day I installed it. Not only JustCode, Sitefinity Thunder and some other Telerik extensions that I have, but also Ankh (I use it at home only), Minifier and so on. The only one that I currenly miss is the "Spell Checker" extension that I really like and saved me many times from deploying typos and nonsense text. I hope that it will be ported, too soon.

Some features I find really useful

Well, For me the winner so far is the search functionality when adding assembly as a reference. It should been there much earlier I think, but better later than never, right? In fact, the whole dialog has been redesigned and the work with it now is much more fluent than before.

The Quick Launch box - although, I haven't utilized it quite good, yet I think this will be something I will use a lot in the future. It just takes some time to break your habits and change the flow you are used to, but this will payback very quickly. The operation you can do from this are numerous and this will be a great performance booster, I think.

The multi-display support is awesome. Much better than before - now I get two tabs out of the main window and snap them - one on the left side of my second monitor and one on the right side. This has to be done manually before and now I can use the win + left arrow and win + right arrow shortcuts to fit the windows where I want them. Moreover, I can work on a tab out of the main window and a word document at the same time, which is again very convenient. 

In the pending changes window, you can apply custom filter to list of files. This way you can much easier check-in all the *.config files for example and ignore all the rest.

The ghost tab, makes my workspace cleaner. Now, I can browse my code with flooding my tab list with tabs that, I don't actually need.

What I still don't like after several weeks work?

Some of the keyboard shortcuts that I'm used to are no longer there. For example, I used to build with F6 and rebuild with Ctrl+F6. For some reason, only Ctrl+Shift+B is available now which is not so comfy to me. I know that I can add them through the settings, but I try to modify them as little as possible in order to maintain the same experience when using VS on another machine. I guess, I will have to live with this.

There are some drastic changes in the pending changes window. It's now part of the Team Explorer window and the design is completely new. You have two different lists with files that will be included in the checkin and those that are excluded which is nice. Assigning work items to the changeset is not so easy though. The checkboxes are now removed and you have to either drag and drop the work item (I needed a week or two to figure what I have to drag and where exactly) or you can enter the work item ID by hand. After adding the work item you should pay attention to the action selected as it is not so noticeable, now. I ended up resolving several work items, when I just wanted association. Since we have a checkin policy that makes assigning work items mandatory - this is the change in the VS 2012 that bothers me most. Probably, I need some more time to get used to.

What I still miss in Visual Studio 2012?

I would greatly appreciate some transition between the previous version of VS and the next one. For example, I had to go and manually set all kind of settings that I used in VS 2010, right after I installed the new version - all the external tools I use, all the extensions, some settings like "Track Active Item in Solution Explorer" and so on. I would be great, if I was prompted to transfer this options from my previous installation and this would make my migration much more fluent and easier. This will allow me to focus on the new features and evaluating the new product, instead of bothering myself "What I missed to add to the new Visual Studio". Probably, this will be a great feature for VS 2015?

And this is shocking - after the second day, I don't pay attention to the ALL CAPS menus!! Even though I can rollback this behavior, very soon after start working with the new VS, I found out that this doesn't stand on my way (but doesn't help me either). I think, it didn't worth the trouble all these people complaining about this, having in mind all the other great enhancements included.

The bottom line is that Visual Studio 2012 is ready to be your primary IDE, today. Being stable and mature enough, I guess that more and more people will start their transition. I'm very interested to read your impression after some real live experience. We all watched the marketing presentation and the cool stuff Microsoft showed already, but what is actually useful for you? Feel free to left your comments bellow. 

Wednesday, May 30, 2012

How did I get started on computers and programming?

In response to Scott Hanselman's post, I decided to tell my story. I would love to hear your story, too.


Pravetz 8
I first touched a computer when I was 5th grade in the elementary school. There was this class called "Computers" that everyone could see and use a computer. The computers were Pravetz 8 machines and there were very little thing you can do with them. I just tried typing on the keyboard, since there were nobody who can show us how to use the computer - I was deeply disappointed. There were also six or seven Macintosh machines that we weren't allowed to touch, because they were too expensive.

Several years after that in the secondary school, a schoolmate of mine told me that he know how to make websites. I got very excited and he land me a booк called "DHTML in action". This was one of the very few books translated in Bulgarian back then. I started reading and become really passionate about web design, html and so on. I was surprised that by typing words, I can add colors, change fonts, show images, make things move around. When I get to the JavaScript part, everything get almost magical - repeating some stuff several times, writing conditional expressions - I felt like being in Alice's Wonderland.

Unfortunately, I didn't have a computer at home. I had to go to Game clubs (they were very popular then) and pay for hour to write html pages and exercise what I read. It was pretty strange, since all the people around me were playing Counter Strike, Unreal Tournament, chatting in mIRC, etc. It was difficult to do what I wanted because, gaming clubs used to have shell around the Windows OS in order to prevent damaging the machines and this really limited my experience. I was amazed to find a website in Bulgarian for web design - Groove Manifesto and this quickly get one of my favourite resources in the net.

One night several months later, me and my brother asked for a PC as a Christmas gift from our parents. Those days a decent PC configuration costed around $700 which was a little fortune here in Bulgaria and not many people were able to afford it. Still, I did a very compelling speech in front them, about how this is going to be my profession one day and that I really need the computer to work and learn, instead of playing games and fooling around. So, this did the trick and we got our first computer.

We were really really happy boys! I remember that we had 32MB NVidia video card, which was the best you could get. Almost immediately, we got a dialup internet which was as well quite a luxury. The first thing I did after connecting, was to download a mp3 - No Leaf Clover by Metallica (yeah!). This PC is still working after several upgrades and I actively used it as a complimentary PC for various stuff until a couple of years.

I started reading and practising even more. I bought a book about C and after that another one about C++ and started writing my first programs using DevC++ compiler. I recall my hard times understanding what an object means and what the h*ll should I do with it. The rest is history - I attended some high school competitions, made my first very lame sites and decided to study informatics in the university.

So, the end of my story should be - Thanks mom and dad  for getting me my first computer and thus making me a programmer! I know that this was the most expensive present I will have (and not only for the money).

I would love to read your story, too so feel free to use the comments form or post a link to your blog post.

Tuesday, May 29, 2012

Adding microdata to your breadcrumb with Sitefinity

As you probably know, microdata is great way for improving your web appearance and adding semantic to your markup. This way, search engines and other automated web engines make greater use of your content, serve it in a better to the users and other various benefits. The microdata format is different for all the content types you can have on your site and today I will write about the breadcrumb.

The Goal

According to the specification, the mark up of your breadcrumb should look like this: 



(The itemscope="" attribute should be actually only itemscope, with no ="" in the end. Probably, blogger + my syntax highlighter script are playing tricks here. Take a look at the specification.)
When you are developing your website directly editing your markup, the task is very simple. Just add the appropriate attributes and their values in your php/html/asp.net or whatever code you use and that's it. When using CMS, you usually will have to tweak something in your setup, most probably there is a template that is used for displaying your breadcrumb. This is what I'm going to show you in Sitefinity.

The Breadcrumb widget in Sitefinity

Unfortunately, you cannot edit this widget through the widget templates page in the administration nor from the Thunder extension (at least, I don't see them for some reason). So what I did is to drag the Breadcrumb widget on a page -> click Edit and you will see the following screen:






You have two choices here: either edit the existing Breadcrumb template, which means that every where on your site the markup will be changed or create new template and apply it on the appropriate places. I took the second approach in order to rollback and test easier possible issues. So, I copied the code from the existing template and created a new one called "Breadcrumb with microdata". Internally the breadcrumb widget uses RadSiteMap control (from Telerik's suite) and you need to change the default template rendered to the following:
<%@ Control Language="C#" %>
<%@ Register Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" TagPrefix="telerik" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sf" %>
<div class="sfBreadcrumbWrp">
<sf:sitefinitylabel cssclass="sfBreadcrumbLabel" hideifnotext="true" id="BreadcrumbLabel" runat="server" wrappertagname="span">
    <telerik:radsitemap id="Breadcrumb" runat="server" skin="Sitefinity">
        <defaultlevelsettings layout="Flow" listlayout-repeatdirection="Horizontal">
          <nodetemplate>
            <div itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="http://www.blogger.com/%3C%# Page.ResolveUrl((string)DataBinder.Eval(Container.DataItem," url="url">" itemprop="url"&gt;
                  <span itemprop="title">&lt;%# DataBinder.Eval(Container.DataItem, "title") %&gt;</span>
                </a>            
              </div>
             </nodetemplate>
         </defaultlevelsettings>
     </telerik:radsitemap>
</sf:sitefinitylabel>
</div>
Mind the 'itemscope', 'itemtype' and 'itemprop' attributes I added. These are the microdata formats that you need for a breadcrumb. Another thing that worth mentioning is that the Url of the data item that is binded is relative url and you need to resolve it using Page.ResolveUrl to an absolute one. Apart from that, things are pretty straightforward. You click the "Save changes" button and from here your changes are already applied to the template. If the template is used somewhere already - the changes are live so you would prefer to try this not on your live environment first.

The final result


Probably, you noticed that in the li element there is a div with class="rsmTemplate" and another div in it with the microdata format. This is because I couldn't find a way to edit the template of the wrapping div. Otherwise, It would be much better to have only one div element. The itemscope="" attribute is a little messed here, because of the highlight script I use, it's actually only itemscope as in the specification above.

When you are ready, you can use the google webmaster rich snippets tool to check whether your markup is correctly decorated with the microdata format. This tool will show your markup, as seen by the google engine and will show you if you have missed something in your html code.

That's all for now. I hope you find this helpful.