Tuesday, March 12, 2013

Have a Blog? Analyze yourself!

According to me, the key to personal improvement is to know yourself well. There are many ways to achieve better self awareness - 360 degree feedback evaluations, various questionnaires, etc. Recently, I stumbled to a great resource - typealyzer.com. It's an amazing tool that analyzes your blog and based on the content - determines part of your psychological profile.

Who am I then?

For those of my readers that don't know me in person, here is my report based on this blog:
"The author of http://www.etabakov.com is of the type ISTJ.
The responsible and hardworking type. In general, ISTJs are capable, logical, reasonable, and effective individuals with a deeply driven desire to promote security and peaceful living. They are especially attuned to the details of life and are careful about getting the facts right. Conservative by nature they are often reluctant to take risks or break any rules.

In relationships ISTJs are extremely loyal, good listeners, extremely good (conservative) with money. As a weakness they tend to believe they are always right.

As reserved and serious individuals, they earn success through their thoroughness and extraordinary dependability. The Duty Fulfillers are happy to be let alone and to be able to work in their own pace. They know what they have to do and how to do it.

Common profession include: Accountant, Data Analysts, Doctor, Business Administrators, Detectives, Judges and Computer Programmers."

It's actually a little scary how accurate this is.

But how Typealyzer works anyway

By analyzing a content that you have written, Typealyzer uses Myers-Briggs theory to determine your profile according their classification. As you can find in www.wikipedia.com, there are 16 available types. These types are mixes between different variations of four pairs of preferences (called dichotomies): Extroversion - Introversion; Sensing - Intuition; Thinking - Feeling; Judging - Perception. So, my type is a mix of - Introversion, Sensing, Thinking, Judging or the stated above ISTJ. For more info: http://en.wikipedia.org/wiki/Myers-Briggs
Actually, Typelyzer is a wordpress based website, that uses an API for analyzing text exposed by www.uclassify.com

How can this be useful

First of all, it's a lot of fun. I already tried all of the bloggers I follow and it's quite interesting to learn a little more about them and their personality. It's even more interesting for those of them that you actually know (or at least you think so). Suddenly, parts of their behavior that seemed strange are not so extraordinary anymore. 
What's more important you have the ability the understand better people that you work with in a very unobstructive way. Knowing what kind of people you work with, can improve your communication and persuasion skills. 

Thursday, February 21, 2013

My favorite #likethemost features in Sitefinity 5.4 release

Some time ago, Sitefinity Team asked the community "What is your favorite improvement?" - so here is my answer. Needless to say, my favorite features in Sitefinity's latest release - 5.4 are the one that our team has requested. As the biggest Telerik internal consumer of Sitefinity, we work closely with the development team to resolve issues that we experience with the CMS and those improvements are always part of the next official release - so our clients can also benefit.

Output caching for RSS

Probably, the biggest producer of RSS feeds from all Telerik sites are blogs.telerik.com. We have more than 30 RSS feeds exposing various subsets of our blogs, which are used by thousands of visitors (and their RSS clients) daily. We expect this improvement to decrease the heat on our servers in terms of CPU load and database queries and in general to boost the performance of our feeds. And best of all - it's super easy to start using this option, just set one of the output cache profiles you have already created in your website:
output cache settings screen


Automatically invalidate browser cache of the theme CSS files when files were changed on the server

This is something that bothered us a lot and especially our customers. The bad news was that we have no way to work around this behavior when using css files from theme. We developed all kind of custom control for other cases - when adding resources in the master page, on the page templates or even in a single page. But when it comes to the automatically added css files from the theme - we were helpless, we had to use primitive approaches like renaming the css file or writing the css so it doesn't break if it is not invalidate right away. This present to our visitors as all kind of horrible effects - broken layout, wrong images on wrong places (because of css sprite issues), etc. Sitefinity 5.4 must put end to this and we are eager to test it. Take a look at the query string on the asset request:


Decreased and optimized the client scripts server by Sitefinity public widget

Now, we don't want to take all credits for this improvement of course the product teams are always keen on performance improvements. And our front end teams are always there to generate new ideas and suggestions. We believe that at this point of the evolution of the CMS this was the biggest bottleneck in the loading and rendering process of the pages. Especially, when a lot of the built in widgets are used on a single page. We can probably came out with a comparison between on of our public sites running on 5.3 and 5.4 once they are upgraded on production. 

Mapping external templates to a widget, created by the Module Builder

This is probably my favorite bug fix from all and I have personally requested it a while ago. It made the work with the module builder really nasty when you need something more specific (especially, in the early days when there were no child parent relationships and you had to implement it yourself). Now, it comes the best part - to remove all user controls names OpenAccessDataProvider,3FDAE414-79B2-11E2-9A1F-A30D6188709B.ascx :) 

And finally - some other goodies that we didn't requested but would love to see them in action

The precompiled widget templates

We upload our websites every two weeks on two web nodes. every improvement that boost the start up time is highly appreciated. We have an upload in the next couple of days and I can't wait to see the results. 

Sitefinity Lightning server module distributed as NuGet package

We are using Lightning internally from the first days and it is now enabled on some of the public sites we had to evaluate it for our internal needs. With the aggressive release schedule that Lightning team has, it was a struggle to always have the latest bits. We are happy to see it as a NuGet package.

The new automated testing tool part of the SDK

This will probably be in my focus are for the next quarter as we are looking for a new automated infrastructure to cover our Sitefinity 5 web assets. As you may know, this is not a trivial task since you can't just unit test your custom functionality when referring the Sitefinity assemblies. I'm going to explore all the capabilities of this tool and probably will get back with some posts on how to use it for testing your own web applications. By the way, there is an interesting webinar coming on this topic on 28 Feb that I'm going to attend. You can also subscribe on http://www.sitefinity.com/developer-network/webinars/upcoming-webinars 

Congrats to the Sitefinity Team for the successful release and many thanks for the support!

Note: With the just announced Roadmap for Sitefinity 6, my  next #likethemost post should be even more exciting.

Tuesday, February 19, 2013

Building my Visual Studio workplace from scratch

I hate reinstalling my development machine, because it's tedious and boring task to make it "as it was before". I have several tools that need to install besides Visual Studio, some extensions in Visual Studio and tons of settings that I prefer than the default one. I would love to see "Upload my machine settings to live.com account" and "Restore my settings from live.com" options in the next Windows releases ... until then we will have to find our way through this challenge.

Visual Studio settings I always change

In the "Source control" settings, I always enable "Get latest version of item on check-out...", "Show deleted items on Source Control Explorer","Get everything when solution is opened" and disable "Attempt to automatically resolve conflicts when they are generated" - the last one can cause a lot of pain.

Additionally, I have to Configure the user tools as I don't really like the default compare and merge tools (even the new one in Visual Studio 2012). I use WinMerge for both operations so I have to add new user tools:
Extension .*; Operation Compare; Command C:\Program Files (x86)\WinMerge\WinMergeU.exe; Arguments /e /u /wl /wr /dl %6 /dr %7 %1 %2

Extension .*; Operation Merge; Command C:\Program Files (x86)\WinMerge\WinMergeU.exe;
Arguments /e /u /wl /dl %6 /dr %7 %1 %2 %4

Something else I usually do is to enable the Line numbers for all languages - not sure why by default they are on for a subset only.

Visual Studio extensions

SlowCheetah - XML Transform is an extension for building configuration transformations for your app.configs or whatever xml files you use.

Spell Checker - another extension that has saved me a lot of times. 

XAML Regions is something I already wrote about how useful could be.

And a bunch of Telerik products' extensions - JustCode, JustMock, OpenAccess, Sitefinity Thunder, etc.

I also add one external tool that executes undo operation to all pending changes that has no changes (the files has only been checked out, but no modified).
Title: Unfo fake changes;
Command %windir%\System32\cmd.exe;
Arguments /C echo y | "%ProgramFiles(x86)%\Microsoft Team Foundation Server 2010 Power Tools\TFPT.EXE" uu /recursive /noget;
Initial directory $(SolutionDir)
All credits for this tool goes to this guy

Storing your Visual Studio settings in the cloud

Recently, I found one awesome feature of JustCode - store your Visual Studio settings in the cloud (this should probably be incorporated on the logo, so everyone can see it). This saved me probably 30% of the work last time I had to pre install my PC. Just make sure to mark all settings for storing - by default some settings that are subject to intellectual property are not selected. And of course it can store your JustCode settings which is quite beneficial as well. What's even better - I can sync my settings on my work PC and on my PC so I can have unified experience no matter where I am. 

One tip that I always forget to follow - after you set up your new environment, create a new workplace in TFS, otherwise your TFS status will be scrambled - Visual Studio will think you have the latest version of the file, while the file is not on your file system at all.

With this post I hope that someone's else reinstallation will be easier and faster. Or at least will give you a tip on how to improve your current set up.

Tuesday, February 12, 2013

HOWTO: Generate C# request object for GitHub hooks

This week I needed to create a web service for synchronizing changes from GitHub repository into our internal environment. This is easily achievable using two great tools, I just came across.

First, GitHub has created requestb.in - which is really awesome development tool. You can create your own bins, which exposes an endpoint for you that tracks all the requests made to it. As GitHub suggested, I added new hook (go to github.com -> settings -> service hooks -> webhook urls) with this bin I created in requestb.in. This way, I received the json object that GitHub sends to the endpoint.

After I got the json object, I used another awesome tool that I'm going to keep in my bookmarks: http://json2csharp.com/ It converts your json object to c# class definition. Much more convenient than writing all the classes from scratch.

Finally, I had to replace some variables that json2csharp identified to be of typed object (basically, because they are empty) with variables of type string. And delete some classes that were duplicates. Here is what I ended up with:

Feel free to use it out of the box or you can use my approach in case GitHub update their API in the future. I hope this will help someone else, too.

Monday, February 4, 2013

Copy and Paste made easier in Sitefinity by eVeliko

eVeliko is a Bulgarian startup company founded by a friend of mine about an year ago. Their first product Copy.Paste.Done is an extension to Sitefinity that enables copying and pasting widgets across different the whole website and even across different websites. We already tried this product internally for some of the Telerik's websites and we are really amazed. Take a look before we continue ...


As a person responsible for the websites, the feature that I like most is the extremely easy installation and deinstallation. Just one assembly added to the bin of your website and you are done. The user interface is exactly what you expect and nothing that stays on your way when you don't need it. I'm sure that non technical people will also embrace this functionality as they will not bother anymore with settings they don't know what to do with.

All front end developers which usually are the people setting the content on our websites, are also very fond of this extension. The deal is that we have to maintain quite a few Sitefinity websites and every site has hundreds or thousands of pages. We often get requests like "I want that functionality on those pages" - then we have to open two browsers side by side and copy all the properties of every widget one by one. Very tedious, cumbersome and most important error prone task. That's why we expect great performance boost, once we start using it in production.

If you have similar problem - I highly recommend at least trying eVeliko's offering. Moreover, If you really like it and it will pay its price for a day - just check out the price. Last but not least, As among the first customers of eVeliko - you will get an enterprise quality support for all kind of questions, problems, feature requests, etc. And be sure to check regularly these guys - as a "friend of eVeliko" I know that they are cooking some pretty nice stuff with a lot of energy and passion, coming soon...

Friday, January 25, 2013

Measuring the success of you blog with Sitefinity

No matter what we do, we always try to be successful. Whether we are selling goods, writing content for the fun of it, raising funds for charity - every day we struggle to be better than yesterday (or at least that's what we should do). And no matter what success means to us - most of the cases it is measurable. Number of items sold, total unique visitors for a month, total views of your youtube video.

When it comes to blogs, measuring success might mean a lot of things - counting the comment if your goal is to raise discussions, new leads in your pipeline if your goal is to sell your product, social networks activities if you want to change the world. But most often - it's a combination of a lot of metrics. It's quite easy to check how many these metric one by one for each post, but ultimately you would like to have this information for all blog posts at one place.

The bad news is that Sitefinity doesn't give you a lot of reporting out of the box. But the good news is that its super easy to be done using the Sitefinity API and some other APIs. To show you how easy it is - I have prepared a very simple report for a Sitefinity website, that shows some metric for blog posts - this can very easily be converted to all kind of content - news, custom modules, just name it.

For the presentation, I will use a simple grid and two datepickers for start and end date of the report:
Start time:
<telerik:RadDatePicker ID="rdpStartTime" runat="server">
</telerik:RadDatePicker>
End time:
<telerik:RadDatePicker ID="rdpEndTime" runat="server" />
<telerik:RadButton runat="server" ID="btnGenerateReport" Text="Generate report" />
<br />
<telerik:RadGrid runat="server" ID="rgCommentActivities" AllowSorting="true">
    <MasterTableView CommandItemDisplay="Top">
        <CommandItemSettings ShowExportToExcelButton="true" ShowAddNewRecordButton="false"/>
    </MasterTableView>
    <ExportSettings>
        <Excel Format="Biff" />        
    </ExportSettings>
</telerik:RadGrid>
Nothing, extraordinary here. I will use the built in capabilities of the telerik grid to export to excel - this way I will be able to create all kind of charts later.

And now to populate the data:
 
private void dataBindGrid()
{
    DateTime startTime = rdpStartTime.SelectedDate ?? DateTime.MinValue;
    DateTime endTime = rdpEndTime.SelectedDate ?? DateTime.MaxValue;
    endTime = endTime.AddDays(1);

    UserManager userManager = UserManager.GetManager();

    BlogsManager blogsManager = BlogsManager.GetManager();
    IList<CommentActivityViewModel> activities = new List<CommentActivityViewModel>();

    foreach (BlogPost blogPost in blogsManager.GetBlogPosts().Where(bp => bp.Parent != null && bp.PublicationDate >= startTime && bp.PublicationDate <= endTime && bp.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live))
    {
        CommentActivityViewModel activityReport = new CommentActivityViewModel()
        {

        };
        activityReport.CommentsCount = blogsManager.GetComments().Where(c => c.CommentedItemID == blogPost.Id && c.CommentStatus == Telerik.Sitefinity.GenericContent.Model.CommentStatus.Published).ToList().Count();
        activityReport.BlogPostUrl = blogPost.ItemDefaultUrl;
        activityReport.Blog = blogPost.Parent.Title;
        activityReport.PublishDate = blogPost.PublicationDate;

        var userProfile = userManager.GetUser(blogPost.Owner);
        if (userProfile != null)
        {
            activityReport.Author = userProfile.UserName;
        }

        activityReport.TwitterCount = getTwitterCounts(blogPost);
        activityReport.FacebookCount = getFaceBookTotalCounts(blogPost);
        activities.Add(activityReport);
    }

    rgCommentActivities.DataSource = activities;
    rgCommentActivities.DataBind();
}

private int getTwitterCounts(BlogPost blogPost)
{
    string urlToCheck = UrlHelper.GetSocialLinkByBlogPostId(blogPost.Id);
    if (!string.IsNullOrEmpty(urlToCheck))
    {
        WebClient webClient = new WebClient();
        string twitterAPI = string.Format("http://urls.api.twitter.com/1/urls/count.json?url={0}", urlToCheck);
        var jsonResponse = webClient.DownloadString(twitterAPI);
        JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
        TwitterCountReponse jsonObj = jsSerializer.Deserialize<TwitterCountReponse>(jsonResponse);
        return jsonObj.count;
    }
    return 0;
}

private int getFaceBookTotalCounts(BlogPost blogPost)
{
    string urlToCheck = UrlHelper.GetSocialLinkByBlogPostId(blogPost.Id);
    if (!string.IsNullOrEmpty(urlToCheck))
    {
        WebClient webClient = new WebClient();
        string facebookAPI = string.Format("https://graph.facebook.com/fql?q=SELECT url, normalized_url, share_count, like_count, comment_count, total_count, commentsbox_count, comments_fbid, click_count FROM link_stat WHERE url='{0}'", urlToCheck);
        try
        {
            var jsonResponse = webClient.DownloadString(facebookAPI);
            JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
            FacebookCountReponse jsonObj = jsSerializer.Deserialize<FacebookCountReponse>(jsonResponse);
            return jsonObj.data[0].total_count;
        }
        catch (Exception e)
        {
            return 0;
        }
    }

    return 0;
}

private string getSocialLink(BlogPost blogPost)
{
    PageManager pageManager = PageManager.GetManager();
    PageNode pageNode = null;
    if (blogPost.Parent.DefaultPageId.HasValue)
    {
        try
        {
            pageNode = pageManager.GetPageNode(blogPost.Parent.DefaultPageId.Value);
        }
        catch (Exception e)
        {
            return "";
        }
    }

    if (pageNode != null && pageNode.Urls.Any())
    {
        if (pageNode.Urls.Any(u => u.IsDefault))
            return string.Format("blogs.telerik.com{0}{1}", pageNode.Urls.Where(u=>u.IsDefault).First().Url.Replace(".aspx", "").Trim('~'), blogPost.ItemDefaultUrl);
        else
            return string.Format("blogs.telerik.com{0}{1}", pageNode.Urls.First().Url.Replace(".aspx", "").Trim('~'), blogPost.ItemDefaultUrl);
    }

    return "";
}
No need of too much explanations here, but still - using the Sitefinity API we retrieve all blog posts within the date filter, that are published. We retrieve their author in case we have multiple publishers in our CMS. Of course, you can additionally add all kind of filters - by blog, by user, by category, etc. You can make use of all the data in Sitefinity, but most certainly you need the comment counts.

 For every blog post we ask Twitter and Facebook APIs for the count of activities for this blog posts. You can get all kind of information from facebook - comments, likes, shares, in case you need a more detailed report. Something that worth pointing out is the getSocialLink function. We use it to get the url for a blog post from its default page (the one that is set in the blog's settings). And everywhere in the site we populate this url into all the social networks, so that we can have all the social info for one URL only.

You can easily extend this report to show linkedin and even better. You can use Google Analytics API to get the pageviews, bounce rate and all kind of analytics data for these posts - this way you will create a one stop solution for your blog posts activities. After you have created the UserControl that you need - you can use the backend pages to create a new page, put it in the navigation (I created a new top level menu - "Reporting") and make it available for you or your users. Probably, the only downside is that this is slow - you call a third party APIs on every iteration and this report might need hours to end if you have thousands of blog posts - still better than  nothing.

I hope this have been helpful!


Tuesday, January 15, 2013

HOWTO: Add semantic markup to automatically published content in Sitefinity

One of my favorite features in Sitefinity 5 is the "Alternative publishing system". Basically, you get a number of inbound and outbound pipes and you can miss and match them in order to publish content in or out of your CMS via different channels. This is extremely convenient and extensible framework, which can be of great help to accomplish not so common scenarios. Just a few examples of what can be achieved - you can easily expose all your blog posts as a RSS feed, you can take a RSS feed and publish the items in it as blog posts, news items, tweet it, etc. What's even better - you can define your own custom inbound and outbound pipes to extend this system and suit it to your needs (if only there were enough documentation about this).

The Scenario

What we need for one of our projects is to automatically publish content from external blogging frameworks into our Sitefinity project. This is easily accomplish with taking RSS feeds from the external platforms and setting them as inbound pipe and publishing them as blog posts. This works greatly out of the box. The problem comes when the search engines index our automatically published content - since this content will be exact duplicate, we have to decorate it the proper way so our SEO rank doesn't suffer. We have to set the following markup in our page, to designate the original source of this content:
<link rel="canonical" href="[original url]" />

Extending the Blogs module

First, we need a new field to keep the source of a blog post. Every post that is automatically published will have this field filled with the url of the original blog post. The rest will have just an empty field. This is easy to be done through the administration: Go to Blogs module -> Click on a random blog -> "Custom fields for posts" -> Click on "Add a field". Choose appropriate name (let's say CanonicalLink) for the field and set it to a short text type. Done.

After that we need to write some logic that adds the suitable markup when this field is not empty. To achieve this, the easiest way I found is to derive from "Telerik.Sitefinity.Modules.Blogs.Web.UI.Public.DetailPostView" class, override the InitializeControls method and implement the logic I need there.

Finally, we need to set this modified class as a detail view for blog posts - this is done again through the administration: Administration -> Settings -> Advanced -> Blogs -> Controls -> BlogPostsFrontend -> Views -> DetailBlogPostsFrontend. There is a field "ViewType" here and you should set the fully qualified typename of the modified class. Hitting save will modify some config files, so they should be checked outand the app pool user should have permission to modify them.

So far so good, if we have the field filled in - the proper markup will be rendered. We need to fill the field now.

Extending the Alternative publishing system

As I mentioned the backbone of this system are inbound pipes and outbound pipes. These are basically the input and the output of the system. For this task, we need to extend the default outbound pipes for Sitefinity content and make it to fill the custom field we already created. So, we derive from the Telerik.Sitefinity.Publishing.Pipes.ContentOutboundPipe :

As you can see, I've override the SetPropertiesThroughPropertyDescriptor method to set the custom field I need. Using reflection (no other way unfortunately), I look for a property called "Link" in the wrapperObj - this is the object Sitefinity provides to the outbound pipe, so the pipe can produce the Sitefinity content.

There is another static method used to register the pipe. Here comes the bad news - I couldn't find a way to replace the default outbound pipe with the modified one, so I had to add a new one. This might be incovenient for the end user and leads to misunderstanding sometimes (probably hiding the default one from the UI with css is an option). Everything I tried with removing the default one or replacing it - leads to runtime errors. So, here is how to add a new outbound pipe - subscribe for the initialized event that Sitefinity exposes and call the static method that is already provided in the outbound pipe:

Well, that's all - after you build and restart you will get your new outbound pipe as an option in the dialog:
 As I said, you will have to use the Enhanced Sitefinity content pipe instead of the default one - Sitefinity content. It will act as default one in any other way, but will fill the original link in the custom field we added. I hope this will help someone. Special thanks to Boyan Barnev for helping me with this one, I wouldn't do it without his help :)