Blog
Displaying the cost of a uCommerce custom IShippingMethodService

Displaying the cost of a uCommerce custom IShippingMethodService

I just found this bog post on how to get the shipping cost for a custom shipping method you may have implemented using IShippingMethodService and thought I would share it here.

http://www.davejsaunders.com/2014/10/20/display-the-cost-of-your-ucommerce-custom-shipping-service.html

A common requirement when displaying shipping methods is to show the price against each, and for the built in uCommerce shipping methods there's a handy GetPriceForCurrency() method on the shipping method object. However if that shipping method is a custom one, it just returns 0.

1var allShippingMethods = TransactionLibrary.GetShippingMethods(country);
2var currency = purchaseOrder.BillingCurrency;
3
4foreach (var shippingMethod in allShippingMethods)
5{
6 var cost = shippingMethod.GetPriceForCurrency(currency);
7}

The correct way to get your shipping price is to manually call your service to get the price to be calculated.

As we don't know which implementation of IShippingMethodService will be used we first need to call GetShippingMethodService() to get it.

After that we just need to populate a new Shipment object we the details our custom method needs and then call the CalculateShippingPrice method.

1var purchaseOrder = TransactionLibrary.GetBasket().PurchaseOrder;
2var allShippingMethods = TransactionLibrary.GetShippingMethods(country);
3
4foreach (var shippingMethod in allShippingMethods)
5{
6 // Get the IShippingMethodService for this ShippingMethod
7 var shippingService = shippingMethod.GetShippingMethodService();
8
9 // Construct a fake shipping method to call the service with
10 var shipment = new Shipment
11 {
12 ShippingMethod = shippingMethod,
13 OrderLines = purchaseOrder.OrderLines,
14 PurchaseOrder = purchaseOrder,
15 ShipmentAddress = purchaseOrder.BillingAddress
16 };
17
18 var shippingMethodPrice
19 = shippingService.CalculateShippingPrice(shipment);
20}
Redirect to https using URL Rewrite

Redirect to https using URL Rewrite

There's always been reasons for pages to be served using https rather than http, such as login pages, payment screens etc. Now more than ever it's become advisable to have entire sites running in https. Server speeds have increased to a level where the extra processing involved in encrypting page content is less of a concern, and Google now also gives a boost to a pages page ranking in Google (not necessarily significant, but every little helps).

If all your pages work in https and http you'll also need to make sure one does a redirect to the other, otherwise rather than getting the tiny page rank boost from Google, you'll be suffering from having duplicate pages on your site.

Redirecting to https with URL Rewrite

To set up a rule to redirect all pages from is relatively simple, just add the following to your IIS URL Rewrite rules.

1<rule name="Redirect to HTTPS" stopProcessing="true">
2 <conditions>
3 <add input="{HTTPS}" pattern="^OFF$" />
4 </conditions>
5 <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
6</rule>

The conditions will ensure any page not on https will be caught and the redirect will do a 301 to the same page but on https.

301 Moved Permanently or 303 See Other

I've seen some posts/examples and discussions surrounding if the redirect type should be a 301 or a 303 when you redirect to https.

Personally I would choose 301 Moved Permanently as you want search engines etc to all update and point to the new url. You've decided that your url from now on should be https, it's not a temporary redirection and you want any link ranking to be transfered to the new url.

Excluding some URL's

There's every chance you don't actually want every url to redirect to https. You may have a specific folder that can be accessed on either for compatibility with some other "thing". This can be accomplished by adding a match rule that is negated. e.g.

1<rule name="Redirect to HTTPS" stopProcessing="true">
2 <match url="images" negate="true" />
3 <conditions>
4 <add input="{HTTPS}" pattern="^OFF$" />
5 </conditions>
6 <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
7</rule>

In this example any url with the word images in would be excluded from the rewrite rule.

Sitecore: Programmatically adding contacts to a list

From Sitecore 8 the EXM module now uses lists to manage mailing lists rather than roles against a user. The built in Subscription form control that comes with EXM has also been updated to add contacts to this list. However the subscription control remains WebForms only, so if you implementing an MVC solution you're going to need to write your own. There's also many other scenarios where you may want to programmatically create and add a contact to a list.

Under the hood, contact lists aren’t even a list at all. Rather they are actually just a Facet on the Contact record that contains the list id's for all the lists the contact is a part of. You can see this by looking in the contacts collection in the analytics mongo db.

Or in the Contacts table in the Reporting SQL db.

If you wanted to add a contact to a list you could in theory just add the relevant tag to the contact record like this:

1public void AddContactToList(Contact contact, Item list)
2{
3 using (new SecurityDisabler())
4 {
5 contact.Tags.Set("ContactLists", list.ID.ToString());
6 }
7}

But I wouldn't. The problem with this approach is your going to miss out any logic that will handle updating the counts of contacts in contact lists. Best to use one of the provided list api's instead.

Adding a contact to a list

Sitecore has a ContactListManager object that has a method to associate contacts with lists. All you need to do is create an instance of it and pass it a list of contacts.

1public void AddContactToList(ContactData contact, ContactList list)
2{
3 ContactListManager listManager = Sitecore.Configuration.Factory.CreateObject("contactListManager", false) as ContactListManager;
4
5 List&lt;ContactData&gt; contactList = new List&lt;ContactData&gt;();
6 contactList.Add(contact);
7
8 listManager.AssociateContacts(list, contactList);
9}

Removing  a contact from a list

Just like adding a contact, there's also a handy method for removing one too.

1public void RemoveContactFromList(ContactData contact, ContactList list)
2{
3 ContactListManager listManager = Sitecore.Configuration.Factory.CreateObject("contactListManager", false) as ContactListManager;
4
5 List&lt;ContactData&gt; contactList = new List&lt;ContactData&gt;();
6 contactList.Add(contact);
7
8 listManager.RemoveContactAssociations(list, contactList);
9}

What's that ContactData object?

Chances are you don't have a ContactData object (Sitecore.ListManagement.ContentSearch.Model.ContactData) and instead probably have a tracking contact (Sitecore.Analytics.Tracking.Contact). For the purposes of adding and removing a contact from a list, all your ContactData object really needs is its identifier, which you can do with the following:

1public ContactData ConvertContactToContactData(Sitecore.Analytics.Tracking.Contact contact)
2{
3 return new ContactData()
4 {
5 Identifier = contact.Identifiers.Identifier
6 };
7}

Sitecore: Sharing field data across languages

This is the third in a series of blog posts covering everything you should need to know for building a multilingual website in Sitecore.

Part 1 - Adding languages for a multilingual site
Part 2 - Translating text in your presentation

In the first two parts to this series I concentrated on how you can setup Sitecore to allow different language versions of content to be entered. In some instances though your content will contain fields which should remain the same across all language versions. This could be for product sku's, dimensions of an object or possibly image fields.

To make a field share it's values over multiple languages, in the template definition tick the shared checkbox against the field.

It's worth noting though that as well as making the field value the same across all languages, it will also be shared between all versions within the language.

Although the interface gives the impression that a field can be the default (versioned), unversioned, shared or unversioned and shared. The value of the unversioned checkbox actually become meaningless once shared has been ticked and there really are only 3 options; Versioned, Unversioned and Shared.