Thursday, October 23, 2014

Google Maps API: How To Add Multiple Location Markers

Overview

If you ever had the opportunity to work with the Google Maps Javascript API v3.0 you would see that everything is very straightforward.  First, you initiate a map, specify the center point, specify a zoom level, or even drop a marker(pin), on a specific address.  All this is basic stuff and used by most websites that require mapping functionalities.  Google Maps likes to deal with latitude and longitude combinations because that is the underlying location standard.  It even provides a tool to geocode natural addresses and convert them into lat/lang combos.

Problem

Even though Google doesn't mind if you pass in lat/lang combos or natural street addresses, lat/lang combos would offer optimal performance because there is no data manipulation or conversion needed.  But that is not always the case in real life.  Sometimes we only have natural addresses that we can use.  No worries, because we can call the geocode function of the API to get back a lat/lang object.  The function to geocode an address and drop a marker on the map can be as simple as this:




But what if you have multiple addresses and need to have markers on the same map?  If you know the lat/lang combos, it is very easy.  Just pass in an array of lat/lang combos like this:



Then iterate though the array and create each marker one by one and associate it with the map.  Now, what if we were only given addresses to work with?  That means we would have to geocode every single one of the addressed to get the associated lat/lang combo.  This approach has two issues.

1) Geocoding is an asynchronous call and does not normally process address by address in the order you expect so going through a simple for loop to continuously call the geocoder will not yield expected results.  Unless you zoom out on the map all the way to the global level, maybe you will see desired results as you see the pins drop one at a time.  But that is not usually desired, you want the map to be centered over the dropped pins and zoomed in as much as possible to fit all the pins on the map so unless you know when the last address in the array has been geocoded, you cannot make the calculations required.

2) Geocoding takes a lot of resources from Google and they frown upon multiple calls happening too frequently so calls will start to fail after a few attempts.  There is no specific number of milliseconds to wait so its a matter of trial and error until the error message is not thrown anymore.  They also expect that service providers of mapping data to provide lat/lang data and not just natural addresses so geocoding addresses really is a last resort.


Solution

How do we get around the issue of the asynchronous calls?  It is quite simple.  Just make sure the next geocode request starts after the success of the current one.  To do this, we need to perform the geocodes recursively and checking for success.  First, define a method that takes in an array of string addresses and an object of type LatLngBounds.



...



You should also define a global array of Markers as a place to store all the markers that have been created for addresses that have been processed already.

Next, we perform the geocoding for an address as usual but upon success we need to store the marker in the array.




Then we extend the LatLngBounds object to include the current lat/lang position that was just calculated.




At the end of all this, you need to pop the current address out of the array and call the function again.





So far this appears to work but then we still run into the issue of performing too many geocoding requests consecutively.  A simple trick can be used to get around this, pause a little in between calls.  You can have this call at the beginning of the method.





This simple function is defined as:




At the end of all the geocode requests we can make use of all the lat/lang positions that have been added to the LatLngBounds object.



Fortunately, the map object has a fitBounds function built in that performs all the calculations need to determine the center point of all the lat/lang locations and the appropriate zoom level based on the canvas size of the map.  The calculated center point location and zoom level will also override anything you set manually when initializing the map.

Conclusion

Again, you should not be too reliant on the geocoding features of the API.  Even if you pay a monthly fee, you still cannot make consecutive calls too frequently and there is still a limit on the number of requests per IP address per day, just more than free users.  Try to use lat/lang combos whenever possible.  If the data provider does not provide this data, then maybe its time to find another provider.



Tuesday, September 16, 2014

Sitecore: How to Detect a Sitecore Website

Introduction

This post is nothing more than to satisfy people's curiosity about websites built on the Sitecore CMS platform.  Assuming you do find yourself at the login page, hacking into the content administration area is strongly discouraged using the default login of admin/b.

Approach 1

We can always attempt the very obvious feature of Sitecore which is the default login page.  This is assuming there was no separation of content authoring and content delivery environments.  Try to browse to:

http://www.domain.com/sitecore/login

If you hit the login page then you have succeeded.  The chances of hitting this page in a production environment is probably close to zero.

Approach 2

You can attempt to modify the browser URL by appending Sitecore-specific paramters and values such as:

?sc_site=(...)
?sc_lang=(en/en-ca/fr-fr/...)
?sc_mode=(normal/edit/...)

If you are able to change languages, bring up page editor, or even hit a default dummy website, then you have succeeded.

Approach 3

You can purposely modify a working URL and see if you hit the default 404 page with a URL that looks something like this:



Approach 4

With an inspection tool, look for the cookies that are accepted by the browser. Two cookies that are used by Sitecore are:



Summary

Keep in mind that developers can choose to hide or modify any of these elements if they really go the distance to hide things from nosy inspectors so none of these are fail-proof but is a good start for most sites. 

Monday, September 15, 2014

Sitecore Upgrade Headaches: Item Buckets

I had to perform a site upgrade from Sitecore 6.6 to 7.0.  The major reason for the upgrade...Item Buckets of course.  The biggest problem created by this upgrade...also Item Buckets.  Take a closer look at the upgrade instructions from Sitecore SDN which includes this warning:




This basically means that you should not be upgrading to Sitecore 7.0 if you were too overly excited about the Item Buckets module which you downloaded in the marketplace before 7.0 came out and when it was just a cool concept.  For those of you who got ahead of yourselves by playing around with it, you are all screwed.

Since there is no way to uninstall modules in Sitecore elegantly, I guess we are all screwed and not able to upgrade to 7.0 right?  Well, just because Sitecore does not officially support it does not mean it will not work.  Sitecore just wants to protect themselves from all your complaints by saying that, no hard feelings.

The headache here is that you already have already referenced assemblies such as Sitecore.ItemBuckets.Kernel.dll whereas the new assemblies created by Sitecore for Item Buckets are named Sitecore.Buckets.dll and similar.

When you refence the old assemblies along with the new ones and you deploy, you will have both sets of assemblies in your bin.  The admin UI will look just fine when you browse around and do your content updates but once you decide to use the cool new search feature, you will see that no results get returned, even AFTER you have rebuilt the search indexes.

If we use an inspection tool such as Firebug to look at what is going on, you will see that there are ambiguous references to search methods that exist in both sets of assemblies.  The quick fix was to delete all the old assemblies except the Sitecore.ItemBucket.UI.dll assembly manually from the bin.  This did the trick.  I think the UI somehow is still stuck in limbo from the manual Item Bucket module so that's why that one assembly has to stay to make it happy.

At this point the search should work but then again we encounter another problem.  You will probably step away from your computer for a little bit and return to find that the search no longer works.  Arggghhh!!!!  Why?!!!!!

Well, apparently now there is a Search.ashx handler error being thrown.  It does not make sense because the right rail menu also calls the same handler to generate facets and it works but the main results are not populating.

This took a little trial and error, but apparently, if the session times out, then Search.ashx will fail for the main results.  An easy fix was to increase the timeout time in IIS from something like 20 minutes to something like 120 minutes and recycling the app pool.

Again, this all does not make much sense but works.  If you are stuck in the situation of a manually installed item buckets module in a 6.x environment and has to upgrade to 7.0, then give these steps a try.


Monday, August 25, 2014

Sitecore Access Control: Adding New Users

If you have ever run into situations where you added a new user but then the user is not able to log in to the UI, it is because the new user is not assigned the role "sitecore content users".  You can either assign that role manually or assign the role "sitecore client authoring" which also includes the "sitecore content users" role.  There are other roles that includes the content users role as well.

Tuesday, July 29, 2014

Sitecore Quick Tip: Image control Resizing

If all else fails in trying to make images resize for the Sitecore sc:image web control, you can always give this a try.  Just change the CssStyle attribute instead of using the built-in width and height attributes.



<sc:Image runat="server" ID="logoImage" Field="header logo" DataSource="<%# DataSource %>" CssStyle="height:10px;width:auto"/>
 



Thursday, July 10, 2014

Sitecore Rant: Lucene Index of System Fields

Lucene Index of System Fields

On a current project, there is a need to index the system language fields.  By default, there is an index that takes care of all items in "/sitecore/system" called "system_index".  This contains everything.  I don't want that.  I just want a list of all the languages so I created a new index and added to the "Sitecore.ContentSearch.Lucene.Indexes.Sharded.Master.config" file.  Simple enough.  I also have to define the fields that I want to have indexed.  To do that we open the "Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config" file and define the fields that we want to index.




Notice that we have two kinds of fields in this list.  We have the default fields on top and three custom fields at the bottom.  For all custom fields that are added post-install, the field names are as is.  For most system fields, there are usually underscores either at the beginning of the name or in the middle.  Sometimes there are two leading underscores.  This is used to indicate system fields that are part of the standard template. The field names for "code_page", "regional_iso_code" and "worldlingo_language_identifier" are default system fields and would make sense to have underscores connecting the words.  Also, they are obtained from the section of excluded fields down below:



Note: Keep in mind that unless you remove the entry from the list of excluded fields, the field will not get indexed even if you tell it to in the previous screenshot.

The regional iso code is a good example to explain my rant.  This basically says to the indexer to ignore this field with the ID.  If you inspect the template for the system language, you can indeed see the ID does match up with the field so you would naturally believe the field name is also correct.  WRONG!  If you run the indexer, the value for this field will always be null because there is no such field with this name.  After debugging and looping through all the fields using item.Fields, we can see that the field name is simply "Regional Iso Code" and not "Regional_iso_code". 

Why would Sitecore decide to use this kind of notation for this field and others while some default fields look like this?



I know that there cannot be spaces in the html element tags but if you use underscores for some fields and camel case for others, it would lead us to believe that the underscores are deliberate and indicate the real field names.  If not, then why not just use camel case for all fields definitions in the excluded list?

Also, there is no way to know what the field names are unless you iterate through item.Fields for all the field names.  If you try looking at the config file above, you will be misguided very often, at least in the section of excluded fields.  The correct index entry for all the fields would be: