Tuesday, March 6, 2018

Sitecore MVC Routing using sitecore pipelines approach

Case Study:

It is very important to separate CMS and CD level MVC routing . When we deploy CD content delivery for any web then it should have very specific routing for the web and most of the cms specific routing should be entrusted to cms solution. It is simple you move all the cms level custom route to custom library and later reference and allow to run at runtime using config patch specific to environment. Like SiteSetting.CMS.Config vs SiteSetting.CD.Config this is will part of continous deployment where it will be picked up during depployment to specific cms and CD environment. By doing this we are separating the responsibility and it help maitain consistent approach specific to cms and cd. This also good for performance of application and it will not conlict with web solution specific to its functionality.

CD with specific to web page
Say http://abc.com/homepage which internally call api via ajax -xhr request say http://abc.com/abc/api/home/Get

CMS with specific to admin or shell page
http://abc.cms/sitecore/admin/api/user this is very specific to cms

Now when you are employing this solution in MVC ensure it is separated for each of this environment. The cms web server will have routing logic loaded at runtime specific to its env without adding extra overload to web CD environment.
Ref:

Implementation Logic

using Sitecore.Diagnostics;
using Sitecore.Pipelines;
using System.Web.Mvc;
using System.Web.Routing;
namespace Abc.Platform.Pipelines.Mvc
{
public class RegisterCustomRoutes
{
public void Process(PipelineArgs args)
{
Log.Info("Sitecore is starting", this);
RegisterRoutes(RouteTable.Routes);
}
public void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"admin controller",
"sitecore/admin/abc/{controller}/{action}",
new { controller = "user", action = "index", id = UrlParameter.Optional }
);
routes.MapRoute(
"admin api",
"sitecore/admin/abc/api/{controller}/{action}",
new { controller = "admin", action = "deleteLogin", id = UrlParameter.Optional }
);
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<!-- For more information on using transformations
see the web.config examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<sitecore>
<pipelines>
<initialize>
<processor type="Abc.Platform.Pipelines.Mvc.RegisterCustomRoutes, Abc.Platform" patch:after="processor[@type='Sitecore.Pipelines.Loader.ShowVersion, Sitecore.Kernel']" xdt:Transform="Insert"/>
</initialize>
</pipelines>
</sitecore>
</configuration>

Monday, March 5, 2018

Keyvault access :Certificate give permission to Protected item

Introduction

If you are working with keyvault, ensure you import client certificate with proper permission for it to work properly in your development or local machine. This is most common error and you won't be able to figure out the issues unless you create standalone console app to troubleshoot. The keyvault will always fail at this method and it goes in infinite loop.
public async Task<string> GetAccessToken(string authority, string resource, string scope)
       {
           var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
           var result = await context.AcquireTokenAsync(resource, _clientAssertionCert);
           return result.AccessToken;
       }

Stackoverflow

https://stackoverflow.com/questions/34812897/how-to-suppress-an-application-is-requesting-access-to-a-protected-item-popup

Important Note:

Web application may not prompt you with above security pop-up unlike console application.

Fix/Workaround

Run ->type  MMC
Go to your user personal certificate and delete existing certificate that is application specific and import again with below checkbox status in place.





Thursday, March 1, 2018

Calculate distance between two Geo location using Google Map API

Introduction

In order to run this proof of concept , we need google api  key,API_Key This can be obtain as per your personal google account credential. create project in developer console in google and generate api_key for you to create proof of concept to consume google api.

Try Out Proof of concepts: Hosted in JSFiddler

Google Developer Console Login


Create Project in Google API Console

Google Distance Matrix API

Google Autocomplete Location API

Production Implication

Create account for BUPA or contact IS support for the same.
FREE QUOTA

Key Implementation things to note

  • Use new google.maps.DistanceMatrixService(); for recommended route.
  • The below example can be useful to find distance for multiple source and destination location
  • You can limit auto complete or restrict to specific countries.   destinationautocomplete.setComponentRestrictions({
  •     'country': ['aus']
  • Use new google.maps.DirectionsService() to calculate alternative routes with legs and steps directions
  • Property setting optimizeWaypoints: true gives accurate distance remove complexity such as turns and other criteria
  • Property setting provideRouteAlternatives: true, helps you find all route path options
  • It used traveling salesman algorithm for optimal map routing with google maps.->optimizeWaypoints

Code Base Implementation

<div>
<h2>
Orign Location
</h2>
</div>
<div id="locationField">
<input id="originautocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text"></input>
</div>
<div>
<h2>
Destination Location
</h2>
</div>
<div id="locationField">
<input id="destinationautocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text"></input>
</div>
<br>
<div>
<input type='button' value="Find travelled distance" onclick="CalculatedRecommededDistance()"></input>
</div>
<br>
<div>
<strong>Recommended Route Total Distance</strong>
</div>
<div id="outputRecommended"></div>
<div>
<strong>Longeest Route Total Distance</strong>
</div>
<div id="output"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=places&callback=initAutocomplete" async defer></script>
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var placeSearch, originautocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
originautocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */
(document.getElementById('originautocomplete')), {
types: ['geocode']
});
// Set initial restrict to the greater list of countries.
originautocomplete.setComponentRestrictions({
'country': ['aus']
});
destinationautocomplete = new google.maps.places.Autocomplete(
(document.getElementById('destinationautocomplete')), {
types: ['geocode']
});
destinationautocomplete.setComponentRestrictions({
'country': ['aus']
});
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
function CalculatedRecommededDistance() {
CalculateDistanceforAllAlternativeRoutes();
var origin = document.getElementById('originautocomplete').value;
var destination = document.getElementById('destinationautocomplete').value;
var geocoder = new google.maps.Geocoder();
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: [origin],
destinations: [destination],
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false,
avoidFerries: false
}, function(response, status) {
var originList = response.originAddresses;
var destinationList = response.destinationAddresses;
var outputDiv = document.getElementById('outputRecommended');
outputDiv.innerHTML = '';
//Display distance recommended value
for (var i = 0; i < originList.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
outputDiv.innerHTML += originList[i] + ' to ' + destinationList[j] +
': ' + results[j].distance.text + ' in ' +
results[j].duration.text + '<br>';
}
}
});
}
function CalculateDistanceforAllAlternativeRoutes() {
var directionsService = new google.maps.DirectionsService();
var start = document.getElementById('originautocomplete').value;
var end = document.getElementById('destinationautocomplete').value;
var method = 'DRIVING';
var request = {
origin: start,
destination: end,
travelMode: google.maps.DirectionsTravelMode[method],
provideRouteAlternatives: true,
unitSystem: google.maps.UnitSystem.METRIC,
optimizeWaypoints: true
};
directionsService.route(request, function(response, status) {
var routes = response.routes;
var distances = [];
for (var i = 0; i < routes.length; i++) {
var distance = 0;
for (j = 0; j < routes[i].legs.length; j++) {
distance = parseInt(routes[i].legs[j].distance.value) + parseInt(distance);
//for each 'leg'(route between two waypoints) we get the distance and add it to
}
//Convert into kilometer
distances.push(distance / 1000);
}
//Get all the alternative distances
var maxDistance = distances.sort(function(a, b) {
return a - b;
});
//Display distance having highest value.
var outputDiv = document.getElementById('output');
outputDiv.innerHTML = Math.round(maxDistance[routes.length - 1]) + " KM";
});
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style><link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"><style>#locationField,
#controls {
position: relative;
width: 480px;
}
#autocomplete {
position: absolute;
top: 0px;
left: 0px;
width: 99%;
}
.label {
text-align: right;
font-weight: bold;
width: 100px;
color: #303030;
}
#address {
border: 1px solid #000090;
background-color: #f0f0ff;
width: 480px;
padding-right: 2px;
}
#address td {
font-size: 10pt;
}
.field {
width: 99%;
}
.slimField {
width: 80px;
}
.wideField {
width: 200px;
}
#locationField {
height: 20px;
margin-bottom: 2px;
}
view raw view.css hosted with ❤ by GitHub