Sunday, 18 December 2016

Facing Required meta tag missing (twitter:text:description) Error in your Blogger?

Are you facing issue in setting up Twitter Card for your blog hosted on Blogger? If yes, then this blog post may help you on this.



You will find below code snippet everywhere to setup Twitter Card for your blog. Add below code snippet before </head> tag in your template.

<!-- twitter card details -->
<meta content='summary' name='twitter:card'/>
<meta content='@ThakkarNilesh' name='twitter:site'/>
<meta expr:content='data:blog.url' name='twitter:url'/>
<meta expr:content='data:blog.pageName' name='twitter:title'/>
<meta expr:content='data:blog.metaDescription' name='twitter:description'/>
<meta expr:content='data:blog.postImageThumbnailUrl' name='twitter:image'/>
<!-- end twitter card details -->

But once you add this code snippet, the very next moment you will below error when you validate your twitter card through card validator at https://cards-dev.twitter.com/validator

meta tag missing (twitter:text:description)

If you are facing similar error then you can fix it by surrounding your code with condition as suggested below.

<!-- twitter card details -->
<meta content='summary' name='twitter:card'/>
<meta content='@ThakkarNilesh' name='twitter:site'/>
<meta expr:content='data:blog.url' name='twitter:url'/>
<meta expr:content='data:blog.pageName' name='twitter:title'/>
<b:if cond='data:blog.metaDescription'>
<meta expr:content='data:blog.metaDescription' name='twitter:description'/>
</b:if>
<meta expr:content='data:blog.postImageThumbnailUrl' name='twitter:image'/>
<!-- end twitter card details -->

Now try to validate your twitter card again at https://cards-dev.twitter.com/validator
Hurray... It's showing twitter card. right?

Now enjoy sharing your twitter card!

Monday, 12 December 2016

Publish Bulk Items in Sitecore using Sitecore Bulk Publishing Tool

You will be agree with the fact that many times you want to publish a huge amount of Sitecore items and unfortunately there is NO way to select multiple items while publishing. So you need to select single item at a time and publish it. This is very time consuming process.

This situation is especially happening with content author team in their day to day life of working with Sitecore to pop and publish the content.

In order to increase efficiency of my content team, I have created a tool or you can say utility to publish bulk items in one go.



I am happy to contribute my 9th Sitecore module to the community.

This is how Bulk Publishing Tool will look like when you install it from Sitecore Marketplace. The package contains one ASPX page named "BulkPublish.aspx" page which will be deployed under Sitecore -> Admin folder when you install this package.


You can give either Sitecore Item Path or IDs separated by new lines in right side text box. Clicking on publishing will show Publishing Summary of given page(s) as shown below.


You can download this tool/utility from Sitecore Marketplace.

Feel free to customize this page as per your Sitecore environment and need.


Tuesday, 29 November 2016

Sitecore Language Checklist Report Tool

Smart programmer hates manual and time consuming task, hence they are always looking for automating those manual boring task.

In-fact that's why you are programmer - to automate such stuff and help your team.

Recently I found that my content team member was asked by client to prepare a report to show all available languages for particular set of items. For example: Prepare a report of all available languages for articles, events, news.

Just imagine that how much time will it take to go and check whether particular language is available or not from 18 languages (yes, we have 18 languages configured for our Sitecore website) and hundreds of articles, events and news.

In order to help my content team to automate this process, I thought that it's good idea to create a Language Report Tool which will show all available languages for particular set of Sitecore items.







This is how report look like. Basically this tool is a kind of Sitecore Language Checklist for all the sub-items of given page.





0 indicates no language version available for that item.
1 indicates  language version available for that item.

You can click on download button to generate report of this language statistics. It will download a complete report in XLS format.


Click here to download Sitecore Package for this tool/utility from Sitecore Marketplace. You simply need to install this package from Sitecore desktop editor, it will deploy the page under Sitecore - Admin folder.

For security reasons, you should place this tool/utility under Sitecore --> Admin folder. Although this page is intelligent enough to  redirect you to login page, if your are not admin and try to access this page.

Hope this will save your lots of time if your client has such crazy requirement :)


Thursday, 17 November 2016

How To Use SkyScanner API in ASP.Net?

In this era of technology, we think that we can get everything on internet. But we forgot that if we don't share then how will we/others get that.

This happens with me as well few weeks ago!

Well, I was given assignment of creating traveler page by using SkyScanner API. I thought that I will be able to do it easily by referring SkyScanner official API documentation but it was not so easy. I tried to find if anyone has shared any code snippet to consume SkyScanner API in ASP.Net but not much luck.

Here is the official SkyScanner API documentation.
https://support.business.skyscanner.net/hc/en-us/articles/210645649-Overview
https://support.business.skyscanner.net/hc/en-us/articles/211308489-Flights-Live-Pricing

So I researched and created traveler page by using SkyScanner API. I then thought let me share my research and code snippets with you in case if you are in the same boat.

Below is the landing page where user can search for flight based on different criteria. User can search for either “Round Trip” or “One Way” trip as per choice.



I created one landing page named default.aspx from where user can search for flights. On clicking search button, user will be redirected to search result page name flight-search.aspx

Airport API:

 Airport API is used to auto suggest airport when user type in “To” and “From” textbox



SkyScanner Flight API:

Skyscanner API is used to pull flight details as per given input. You can study API at official documentation https://support.business.skyscanner.net/hc/en-us/articles/211308489-Flights-Live-Pricingfor further details.






Search Result Page:

This will call Skyscanner API and will display result like this



Search Result Filters:

I have also implemented filters functionality which you can explore by looking into code and integrate it as per business requirement. Below screen shows all NON-STOP flights.



Below screen shows all fly-dubai flight as per filter. Clicking on “Book” button will redirect to actual airlines website to book the ticket.



How To Test SkyScanner API


Option-1: Use Postman

You can use any tool to test SkyScanner API, but my favorite one it “Postman” which is getting used by industry.

1)  Install Postman or any other tool as per your choice to test SkyScanner API



2) Send GET request and read “Location” field in HEADER from response.

var URL = "http://partners.api.skyscanner.net/apiservices/pricing/uk1/v1.0/" + sessionKey + "?apiKey=" + "prtl6749387986743898559646983194";




3) Now send POST request with all input values.

poststring = String.Format("apiKey={0}&country={1}&currency={2}&locale={3}&originplace={4}&destinationplace={5}&outbounddate={6}&adults={7}", "prtl6749387986743898559646983194", "AE", "AED", "en-US", fromAirPort, ToAirPort, oneWayDepartDate.ToString("yyyy-MM-dd"), passengers);



Option 2: Use SkyScanner Test Harness page:

SkyScanner has provided on test harness page where you can test your API key with all search paratmters to check flight search results.

This test harness page can be found at:




If you are looking for a complete SkyScanner API project or more details then get in touch with me by posting comment or send an email.

Hope this post will help you get started with SkyScanner API in .NET !

Thursday, 29 September 2016

A Journey To Sitecore Symposium and MVP Summit 2016!

Hope all of you know that what is Sitecore Symposium? If you don't know then most probably you are new to Sitecore or your are not in the world of Sitecore :) 

Well, I just came back from Sitecore Symposium Event which is the must to attend event. Every year Sitecore organizes annual summit. During early years, it was known as Sitecore Dreamcore event. But now it's called as Sitecore Symposium event. Usually Sitecore arranges this Symposium event every year in Sep-Oct month followed by Sitecore MVP Summit.

Sitecore MVP Summit is free event and only for Sitecore MVPs while Sitecore Symposium is paid event and open for all. But if you Sitecore MVP then you will get some discount on Sitecore Symposium event.

221 Sitecore MVPs from 25 Countries






This year Sitecore MVP Summit and Sitecore Symposium was held in New Orleans, Louisiana, USA from 12th Sep to 16th Sep. It was my dream to be part of this event, I was following this event from many years, was reading the blog posts, was seeing event photos, following tweets. This year 2016 is very lucky for me, I got a chance to attend this event and I am going to share my journey to Sitecore Symposium and MVP Summit with you today.

I don't want to make this blog post boaring by writing much text. So I am going to share few clicks of Sitecore MVP Summit and Symposium journey with you.

One of the best part about this event is - you get a chance to meet with Sitecore team. You must be knowing these two pillars of Sitecore - Michael Seifert (left) and Lars FlΓΈe Nielsen (right).


Pieter Brinkman - the man behind arranging Sitecore MVP Summit.


With Yogesh, Mark Stiles and Chris.


With Robbert Hock and Akshay Sura.




With the Sitecore MVC expert lady - Martina Welander.


Finally MVP Summit 2016 is going to start.


Sitecore MVP Summit Day 2 - Going for boating with old colleagues Yogesh and Brijesh.




Got Tattoo in boat on Sitecore MVP Summit Day-2 outing. My quote for this tatto is "Sitecore is very handy".


 Finally got realistic sketch :) !



Got a Sitecore XP book as a goodie on the table in early morning breakfast.


After Sitecore MVP Summit event, getting ready for Sitecore Symposium!





And here you go.. What a grand opening of Sitecore Symposium 2016!!!







Post Symposium Trip


Do you know this place? Well, I planned to see Las Vegas during my this trip and coincidently Sitecore announced on the last day of event that Symposium 2017 is going to be held at Las Vegas. Wow... Isn't it exciting?


See you again next year at Las Vegas on 16th Oct, 2017 in Sitecore Symposium!


Tuesday, 30 August 2016

Sitecore Publishing Restriction Module

All of Sitecore developers know that we can restrict publishing for particular Sitecore item. Now if you set publishing restriction for particular item and try to publish that item with subitems, it will not publish that item but it will publish all subitems as there is NO restrction on subitems.

So there should be an option by which we can restict subitems publishing as well. Below are some examples which made me think about this publishing restriction option.

Sometimes content author or publisher publishes Home item with subitems by mistake
Sometimes  content author or publisher publishes Media Library item with subitems by mistake.

This mistake not only slowed down our stage server but also started publishing all unwanted items.



I have come across this situation many times when my client complains that somebody from content team published Home item with subitems by mistake. This mistake may create disaster for you by publishing all unwanted items on web database which eventually mess up your live website.

So we need to jump to stage server to restart app pool in order to stop publishing. But this solution is not at all accepted.

Moreover, there is no such feature in Sitecore out of the box which can help us to disable publishing for root item with subitems such as "Home" item. This leads me to write an implementation of disabling publish for root items. Then I got an idea - why not write a module which can help our Sitecore community friends who are in the same boat?

Please note that you may have other options but every project has its own existing environment and implemention and sometimes you need to come up with option which can fit into existing implemention.


That's how Sitecore Publishing Restriction Module gets created.
But wait... you may have question  "Have you checked Sitecore MarketPlace before creating this module?"

The answer is - Yes. I have checked and there is a module named "Sitecore Publishing Exclusions" but sadly it doesn't support Sitecore 7 version. Hence I needed to write this module which is supported by Sitecore 6+, 7+ and 8+.

This is a very userful module which will help you to disable or set warning on publishing for all those items which you want to restrict.




As per above configuration whenever you try to publish "Sitecore", "Content" or "Home" item, it will show you warning message. This will eliminate accidently publish without seeing which item is selected.



Moreover, you can completely disable publishing option also for such items. You simply need to uncheck show warning checkbox.

You can also warn your content publisher users about heavy publishing by configuring Heavy Publishing Restriction options. This feature will be very helpful for Sitecore 6+ and 7+ as there is no extra warning message pop-up like in Sitecore 8 publish. Thanks to my collegue Ashish Bansal for giving idea to add this feature.



You can download this module at Sitecore MarketPlace
https://marketplace.sitecore.net/Modules/S/Sitecore_Publishing_Restriction.aspx?sc_lang=en

Once you download and install this module, you need to do a little configuration change suggested below.


Need to comment out existing publish command and add new custom publish command as shown below.

<!--<command name="item:publish" type="Sitecore.Shell.Framework.Commands.PublishItem,Sitecore.Kernel" />-->
  <command name="item:publish" type="Sitecore.SharedSource.CustomPublish,Sitecore.SharedSource"/>

That's it, now you are good to go!

Wednesday, 17 August 2016

How To Track Twitter Emojis Real Time?

In previous post we talked about "How to do live streaming and tracking the tweets?" If you haven't read that post, then I would recommend to Devour it first before moving further.


You will find many articles on internet about live streaming and tracking the tweets, but you will rarely find a way to track twitter emojis real time. There is only one example of tracking twitter emojis real time and it is http://emojitracker.com/

This tracker is awesome and shows live emojis getting used world wide. I have simillar requirement with a small change. My requirement was to track emojis for particular city and show it real time to decide the mood of city.

For example: My client wanted to know the mood of city on internet by tracking emojis on twitter.



Now the link that I shared above (http://emojitracker.com/) was developed on non .NET platform and it was not easy for me to use. Moreover I haven't found any article to track twitter emojis real time using .NET, hence after good amount of research and development I was able to achieve this real time emoji tracking. I am writing this blog post to help my friends who has similar requirement.

It's easy to track tweet based on different criteria such as location and text using TweetInvi API. But the challenge was "How to track emojis?" as it is NOT text. Moreover I needed to store count of these emjois in order to know the mood of cities.

Finally this is how it was achieved.

Steps:

1) Connect with TweetInvi API and track tweets based on your location (e.g. Dubai in my case)


2) Track twitter emojis by comparing emojis unicode with tweet text. You can a complete list of all           twitter emojis at http://unicode.org/emoji/charts/full-emoji-list.html

3) Store the count of different emojis in database to decide mood of the city.



Finally we created a nice dashboard page which will show LIVE mood of the city. You can also check mood of the city at particular time by changing filter criteria.



Here is the complete code set of above steps.

 
 class Program
    {
        static SqlConnection con;
        static SqlCommand cmd;
        static void Main(string[] args)
        {
            Auth.SetUserCredentials("key1", "key2", "key3", "key4");

            con = new SqlConnection(ConfigurationManager.ConnectionStrings["sqlCon"].ConnectionString);
            if (con.State != ConnectionState.Open)
                con.Open();
            using (var webClient = new WebClient())
            {
                webClient.Proxy = WebRequest.DefaultWebProxy;
                webClient.Credentials = System.Net.CredentialCache.DefaultCredentials; ;
                webClient.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
                webClient.Headers["User-Agent"] = "MOZILLA/5.0 (WINDOWS NT 6.1; WOW64) APPLEWEBKIT/537.1 (KHTML, LIKE GECKO) CHROME/21.0.1180.75 SAFARI/537.1";

                var creds = new TwitterCredentials("key1", "key2", "key3", "key4");

                var stream = Stream.CreateFilteredStream();
                var dubaiCords = new Location(new Coordinates(51.596696, 24.369107), new Coordinates(56.348645, 25.005892));                

                stream.AddLocation(dubaiCords);                
                stream.MatchingTweetReceived += (sender, a) =&gt;
                {
                    if (a.Tweet.CreatedAt.Day == DateTime.Now.Day)
                    {
                        try
                        {
                            InsertTweet(a.Tweet.Text, a.Tweet.CreatedBy.Location == null ? string.Empty : a.Tweet.CreatedBy.Location);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error "  + ex.ToString());
                        }
                        
                    }
                };

                var StreamThread = new Thread(() =&gt; stream.StartStreamMatchingAnyCondition());
                StreamThread.Start();

                Console.Read();
                con.Close();

                cmd.Dispose();
            }
        }

        public static void InsertTweet(string tweet, string location)
        {
            Console.WriteLine("Streaming...");
            cmd = new SqlCommand("UpdateMood", con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Clear();

            cmd.Parameters.AddWithValue("@Hour", DateTime.Now.Hour);
            cmd.Parameters.AddWithValue("@Date", DateTime.Now.ToString("yyyy-MM-dd"));

            if (tweet.Contains("πŸ˜€") || tweet.Contains("😁") || tweet.Contains("πŸ˜‚") || tweet.Contains("πŸ˜ƒ") || tweet.Contains("πŸ˜„") || tweet.Contains("πŸ˜†")
                || tweet.Contains("πŸ˜‰") || tweet.Contains("😎") || tweet.Contains("😍") || tweet.Contains("☺") || tweet.Contains("πŸ™‚") || tweet.Contains("πŸ˜‡")
                || tweet.Contains("πŸŽ‰") || tweet.Contains("😊") || tweet.Contains("πŸ˜‹") || tweet.Contains("😘") || tweet.Contains("πŸ˜—") || tweet.Contains("πŸ˜™")
                || tweet.Contains("😚") || tweet.Contains("☺") || tweet.Contains("😜") || tweet.Contains("😝"))
            {                
                cmd.Parameters.AddWithValue("@Mood", "Happy");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet,location);
            }

            if (tweet.Contains("πŸ™") || tweet.Contains("😦") || tweet.Contains("πŸ™") || tweet.Contains("😠") || tweet.Contains("😑") || tweet.Contains("πŸ’’")
                || tweet.Contains("πŸ—―") || tweet.Contains("πŸ˜₯") || tweet.Contains("😫") || tweet.Contains("πŸ˜”") || tweet.Contains("πŸ˜•")
                || tweet.Contains("☹") || tweet.Contains("πŸ˜–") || tweet.Contains("😞") || tweet.Contains("😟") || tweet.Contains("😒")
                || tweet.Contains("😭") || tweet.Contains("😦") || tweet.Contains("😧") || tweet.Contains("😨") || tweet.Contains("😩")
                || tweet.Contains("😰"))
            {             
                cmd.Parameters.AddWithValue("@Mood", "Stressed");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet, location);
            }

            if (tweet.Contains("✍") || tweet.Contains("✍🏼") || tweet.Contains("πŸ“”") || tweet.Contains("πŸ“•") || tweet.Contains("πŸ“–") || tweet.Contains("πŸ“—")
                || tweet.Contains("πŸ“˜") || tweet.Contains("πŸ“™") || tweet.Contains("πŸ“š") || tweet.Contains("πŸ““"))
            {             
                cmd.Parameters.AddWithValue("@Mood", "Studying");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet, location);
            }

            if (tweet.Contains("πŸ”") || tweet.Contains("🍟") || tweet.Contains("πŸ•") || tweet.Contains("🍲") || tweet.Contains("🍚") || tweet.Contains("πŸ›")
                || tweet.Contains("🍜") || tweet.Contains("🍝") || tweet.Contains("🍠") || tweet.Contains("🍣") || tweet.Contains("🍽") || tweet.Contains("🍷")
                || tweet.Contains("🍸") || tweet.Contains("🍴"))
            {             
                cmd.Parameters.AddWithValue("@Mood", "Hungry");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet, location);
            }

            if (tweet.Contains("πŸ’»") || tweet.Contains("πŸ–₯") || tweet.Contains("πŸŽ₯") || tweet.Contains("πŸ“Ί") || tweet.Contains("▶") || tweet.Contains("◀"))                
            {                
                cmd.Parameters.AddWithValue("@Mood", "Youtubing");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet, location);
            }

            if (tweet.Contains("⚽") || tweet.Contains("⚾") || tweet.Contains("πŸ€") || tweet.Contains("🏈") || tweet.Contains("πŸ‰") || tweet.Contains("πŸ“£")
                || tweet.Contains("πŸ“―"))
            {                
                cmd.Parameters.AddWithValue("@Mood", "Cheering");
                cmd.ExecuteNonQuery();
                InsertTweetEntry(tweet, location);
            }

        }

        private static void InsertTweetEntry(string tweet, string location)
        {
            //Finally record the tweet
            cmd = new SqlCommand("InsertTweet", con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Clear();
            cmd.Parameters.AddWithValue("@Tweet", tweet);
            cmd.Parameters.AddWithValue("@Location", location);
            int index = cmd.ExecuteNonQuery();            
        }
    }    



Hope you liked it. Stay tuned for more such posts!